Coverage Report

Created: 2026-07-25 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntopng/src/Ntop.cpp
Line
Count
Source
1
/*
2
 *
3
 * (C) 2013-26 - ntop.org
4
 *
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
 *
20
 */
21
22
#include "ntop_includes.h"
23
24
#ifdef WIN32
25
#include <shlobj.h> /* SHGetFolderPath() */
26
#else
27
#include <ifaddrs.h>
28
#include <sys/file.h>
29
#endif
30
31
/* WebAuthn / Passkey: OpenSSL EC/ECDSA and SHA-256 */
32
#include <openssl/sha.h>
33
#include <openssl/ec.h>
34
35
#ifdef HAVE_OPENSSL_CORE_NAMES_H
36
#include <openssl/ecdsa.h>
37
#include <openssl/evp.h>
38
#include <openssl/core_names.h>   // OSSL_PKEY_PARAM_GROUP_NAME, etc.
39
#include <openssl/param_build.h>  // OSSL_PARAM_BLD
40
#include <openssl/bn.h>
41
#endif
42
43
#include <vector>
44
45
Ntop* ntop;
46
47
static const char* dirs[] = {
48
    NULL, /* Populated at runtime */
49
    NULL, /* Populated at runtime for WIN32 builds */
50
    CONST_ALT_INSTALL_DIR,
51
    CONST_ALT2_INSTALL_DIR,
52
#ifndef WIN32
53
    CONST_DEFAULT_INSTALL_DIR, /* Last is the <path> specified with ./configure
54
                                  --prefix <path>, defaulting to /usr/local */
55
#endif
56
    NULL};
57
58
extern struct keyval string_to_replace[]; /* LuaEngine.cpp */
59
60
/* ******************************************* */
61
62
4
Ntop::Ntop(const char* appName) {
63
4
  if (trace_new_delete)
64
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
65
66
4
  ntop = this;
67
4
  globals = new (std::nothrow) NtopGlobals();
68
4
  extract = new (std::nothrow) TimelineExtract();
69
4
  num_active_lua_vms = 0;
70
4
  offline = false;
71
4
  forced_offline = false;
72
4
  pa = NULL;
73
#ifdef WIN32
74
  myTZname = strdup(_tzname[0] ? _tzname[0] : "CET");
75
#else
76
4
  myTZname = strdup(tzname[0] ? tzname[0] : "CET");
77
4
#endif
78
4
  custom_ndpi_protos = NULL;
79
4
  prefs = NULL, redis = NULL;
80
81
4
#ifdef HAVE_ZMQ
82
4
#ifndef HAVE_NEDGE
83
4
  export_interface = NULL;
84
4
  zmqPublisher = NULL;
85
4
#endif
86
4
#endif
87
4
  trackers_automa = NULL;
88
4
  num_cpus = -1;
89
4
  num_defined_interfaces = 0;
90
4
  iface = NULL;
91
4
  start_time = last_modified_static_file_epoch = 0,
92
4
  epoch_buf[0] = '\0'; /* It will be initialized by start() */
93
4
  last_stats_reset = 0;
94
4
  old_iface_to_purge = NULL;
95
4
  broadcast_ip_disabled = false;
96
4
  flow_id = 0;
97
4
  flow_id_initialized = false;
98
99
4
  setZoneInfo();
100
101
  /* Checks loader */
102
4
  flowChecksReloadInProgress = true; /* Lazy, will be reloaded the first time
103
                                        this condition is evaluated */
104
4
  hostChecksReloadInProgress = true;
105
4
  flow_checks_loader = NULL;
106
4
  host_checks_loader = NULL;
107
108
  /* Flow alerts exclusions */
109
#ifdef NTOPNG_PRO
110
  num_flow_exporters = num_flow_interfaces = 0;
111
  alertExclusionsReloadInProgress = true;
112
  alert_exclusions = alert_exclusions_shadow = NULL;
113
#endif
114
115
  /* Host Pools reload - Interfaces initialize their pools inside the
116
   * constructor */
117
4
  hostPoolsReloadInProgress = false;
118
119
4
  httpd = NULL, geo = NULL, mac_manufacturers = NULL;
120
4
  memset(&cpu_stats, 0, sizeof(cpu_stats));
121
4
  cpu_load = 0;
122
4
  system_interface = NULL;
123
4
  interfacesShuttedDown = false;
124
4
#ifndef WIN32
125
4
  cping = NULL, default_ping = NULL;
126
4
  ping_initialized = false;
127
4
#endif
128
4
  privileges_dropped = false;
129
4
  can_send_icmp = Utils::isPingSupported();
130
131
524k
  for (int i = 0; i < CONST_MAX_NUM_NETWORKS; i++)
132
524k
    local_network_names[i] = local_network_aliases[i] = NULL;
133
4
  local_network_max_id = -1;
134
135
4
  internal_alerts_queue =
136
4
      new (std::nothrow) FifoSerializerQueue(INTERNAL_ALERTS_QUEUE_SIZE);
137
138
4
  resolvedHostsBloom = new (std::nothrow) Bloom(NUM_HOSTS_RESOLVED_BITS);
139
140
#ifdef WIN32
141
  if (SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT,
142
                      working_dir) != S_OK) {
143
    strncpy(working_dir, "C:\\Windows\\Temp\\ntopng",
144
            sizeof(working_dir));  // Fallback: it should never happen
145
    working_dir[sizeof(working_dir) - 1] = '\0';
146
  } else {
147
    int l = strlen(working_dir);
148
149
    snprintf(&working_dir[l], sizeof(working_dir), "%s", "\\ntopng");
150
  }
151
152
  // Get the full path and filename of this program
153
  if (GetModuleFileName(NULL, startup_dir, sizeof(startup_dir)) == 0) {
154
    startup_dir[0] = '\0';
155
  } else {
156
    for (int i = (int)strlen(startup_dir) - 1; i > 0; i--) {
157
      if (startup_dir[i] == '\\') {
158
        startup_dir[i] = '\0';
159
        break;
160
      }
161
    }
162
  }
163
164
  snprintf(install_dir, sizeof(install_dir), "%s", startup_dir);
165
166
  dirs[0] = startup_dir;
167
  dirs[1] = install_dir;
168
#else
169
  /* Note: working_dir folder will be created lazily, avoid creating it now */
170
4
  if (Utils::dir_exists(
171
4
          CONST_OLD_DEFAULT_DATA_DIR)) /* keep using the old dir */
172
0
    snprintf(working_dir, sizeof(working_dir), CONST_OLD_DEFAULT_DATA_DIR);
173
4
  else
174
4
    snprintf(working_dir, sizeof(working_dir), CONST_DEFAULT_DATA_DIR);
175
176
  // umask(0);
177
178
4
  if (getcwd(startup_dir, sizeof(startup_dir)) == NULL)
179
0
    ntop->getTrace()->traceEvent(
180
0
        TRACE_ERROR, "Occurred while checking the current directory (errno=%d)",
181
0
        errno);
182
183
4
  dirs[0] = startup_dir;
184
185
4
  install_dir[0] = '\0';
186
187
28
  for (int i = 0; i < (int)COUNT_OF(dirs); i++) {
188
24
    if (dirs[i]) {
189
16
      char path[MAX_PATH + 32];
190
16
      struct stat statbuf;
191
192
16
      snprintf(path, sizeof(path), "%s/scripts/lua/index.lua", dirs[i]);
193
16
      fixPath(path);
194
195
16
      if (stat(path, &statbuf) == 0) {
196
0
        snprintf(install_dir, sizeof(install_dir), "%s", dirs[i]);
197
0
        break;
198
0
      }
199
16
    }
200
24
  }
201
4
#endif
202
203
4
  setScriptsDir();
204
205
#ifdef NTOPNG_PRO
206
  pro = new (std::nothrow) NtopPro();
207
#else
208
4
  pro = NULL;
209
4
#endif
210
211
4
  address = NULL;
212
213
4
#ifndef HAVE_NEDGE
214
4
  refresh_ips_rules = false;
215
4
#endif
216
217
  // printf("--> %s [%s]\n", startup_dir, appName);
218
219
4
  initTimezone();
220
4
  ntop->getTrace()->traceEvent(TRACE_INFO, "System Timezone offset: %+ld",
221
4
                               time_offset);
222
223
4
  udp_socket = Utils::openSocket(AF_INET, SOCK_DGRAM, 0, "Ntop UDP");
224
225
4
#ifndef WIN32
226
4
  setservent(1);
227
228
4
  startupLockFile = -1;
229
4
#endif
230
231
#ifdef HAVE_SNMP_TRAP
232
  trap_collector = NULL;
233
#endif
234
235
  /* Internal chec to make sure everything is in order */
236
4
  if (!FlowRiskAlerts::checkConsistency()) {
237
0
    ntop->getTrace()->traceEvent(
238
0
        TRACE_ERROR,
239
0
        "Fatal error: nDPI risk alerts are out of sync with ntopng");
240
0
    exit(0);
241
0
  }
242
243
#ifdef NTOPNG_PRO
244
  ifRoles = ifRoles_shadow = NULL;
245
#endif
246
247
4
  bgp = NULL;
248
4
}
249
250
/* ******************************************* */
251
252
#ifndef WIN32
253
254
0
void Ntop::lockNtopInstance() {
255
0
  char lockPath[MAX_PATH + 8];
256
0
  struct flock lock;
257
0
  struct stat st;
258
259
0
  if ((stat(working_dir, &st) != 0) || !S_ISDIR(st.st_mode)) {
260
0
    ntop->getTrace()->traceEvent(TRACE_DEBUG, "Working dir does not exist yet");
261
0
    return;
262
0
  }
263
264
0
  snprintf(lockPath, sizeof(lockPath), "%s/.lock", working_dir);
265
266
0
  lock.l_type = F_WRLCK;    /* read/write (exclusive versus shared) lock */
267
0
  lock.l_whence = SEEK_SET; /* base for seek offsets */
268
0
  lock.l_start = 0;         /* 1st byte in file */
269
0
  lock.l_len = 0;           /* 0 here means 'until EOF' */
270
0
  lock.l_pid = getpid();    /* process id */
271
272
0
  if ((startupLockFile = open(lockPath, O_RDWR | O_CREAT, 0666)) < 0) {
273
0
    ntop->getTrace()->traceEvent(TRACE_ERROR,
274
0
                                 "Unable to open lock file %s [%s]", lockPath,
275
0
                                 strerror(errno));
276
0
    exit(EXIT_FAILURE);
277
0
  }
278
279
0
  if (fcntl(startupLockFile, F_SETLK, &lock) <
280
0
      0) { /** F_SETLK doesn't block, F_SETLKW does **/
281
0
    ntop->getTrace()->traceEvent(TRACE_ERROR,
282
0
                                 "Another ntopng instance is running...");
283
0
    exit(EXIT_FAILURE);
284
0
  }
285
0
}
286
287
#endif
288
289
/* ******************************************* */
290
291
/*
292
  Setup timezone differences
293
294
  We call it all the time as daylight can change
295
  during the night and thus we need to have it "fresh"
296
*/
297
298
4
void Ntop::initTimezone() {
299
#ifdef WIN32
300
  time_offset = -_timezone;
301
#else
302
4
  time_t t = time(NULL);
303
4
  struct tm* l = localtime(&t);
304
305
4
  time_offset = l->tm_gmtoff;
306
4
#endif
307
4
}
308
309
/* ******************************************* */
310
311
4
Ntop::~Ntop() {
312
4
  u_int32_t scan_limit = (u_int32_t)(getMaxLocalNetworksID() + 1);
313
314
4
  if (trace_new_delete)
315
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "[delete] %s", __FILE__);
316
317
12
  for (u_int32_t i = 0; i < scan_limit; i++) {
318
8
    if (local_network_names[i] != NULL) free(local_network_names[i]);
319
8
    if (local_network_aliases[i] != NULL) free(local_network_aliases[i]);
320
8
  }
321
322
4
  if (httpd)
323
0
    delete httpd; /* Stop the http server before tearing down network interfaces
324
                   */
325
326
  /* The free below must be called before deleting the interface */
327
4
  if (flow_checks_loader) delete flow_checks_loader;
328
4
  if (host_checks_loader) delete host_checks_loader;
329
330
4
  for (int i = 0; i < num_defined_interfaces; i++) {
331
0
    if (iface[i]) {
332
0
      delete iface[i];
333
0
      iface[i] = NULL;
334
0
    }
335
0
  }
336
337
4
  if (zoneinfo) free(zoneinfo);
338
339
4
  delete[] iface;
340
341
4
  if (system_interface) delete system_interface;
342
343
4
  if (extract) delete extract;
344
345
4
#ifndef WIN32
346
4
  if (cping) delete cping;
347
4
  if (default_ping) delete default_ping;
348
349
4
  for (std::map<std::string /* ifname */, Ping*>::iterator it = ping.begin();
350
4
       it != ping.end(); ++it)
351
0
    delete it->second;
352
4
#endif
353
354
4
  Utils::closeSocket(udp_socket);
355
356
4
  if (trackers_automa) ndpi_free_automa(trackers_automa);
357
4
  if (custom_ndpi_protos) free(custom_ndpi_protos);
358
4
  if (old_iface_to_purge) delete old_iface_to_purge;
359
360
4
  delete address;
361
362
4
  if (pa) delete pa;
363
4
  if (geo) delete geo;
364
4
  if (mac_manufacturers) delete mac_manufacturers;
365
4
#ifndef HAVE_NEDGE
366
4
#ifdef HAVE_ZMQ
367
4
  if (zmqPublisher) delete zmqPublisher;
368
4
#endif
369
4
#endif
370
371
#ifdef NTOPNG_PRO
372
  if (pro) delete pro;
373
  if (alert_exclusions) delete alert_exclusions;
374
  if (alert_exclusions_shadow) delete alert_exclusions_shadow;
375
#endif
376
377
4
  if (resolvedHostsBloom) delete resolvedHostsBloom;
378
4
  delete internal_alerts_queue;
379
380
4
  if (redis) {
381
4
    delete redis;
382
4
    redis = NULL;
383
4
  }
384
385
4
  if (prefs) {
386
4
    delete prefs;
387
4
    prefs = NULL;
388
4
  }
389
390
4
  if (globals) {
391
4
    delete globals;
392
4
    globals = NULL;
393
4
  }
394
395
4
  if (myTZname) free(myTZname);
396
#ifdef HAVE_NEDGE
397
  for (auto it = multicastForwarders.begin(); it != multicastForwarders.end();
398
       ++it) {
399
    delete (*it);
400
  }
401
#endif
402
403
#ifdef HAVE_RADIUS
404
  if (radiusAcc) delete radiusAcc;
405
#endif
406
407
#ifdef NTOPNG_PRO
408
  if (oidcAuth) delete oidcAuth;
409
#endif
410
411
#ifdef HAVE_SNMP_TRAP
412
  if (trap_collector) delete trap_collector;
413
#endif
414
415
56
  for (u_int i = 0; i < device_max_type; i++) {
416
52
    DeviceProtocolBitmask* b = getDeviceAllowedProtocols((DeviceType)i);
417
52
    ndpi_bitmask_free(&b->clientAllowed);
418
52
    ndpi_bitmask_free(&b->serverAllowed);
419
52
  }
420
421
4
  if(bgp) delete bgp;
422
  
423
#ifdef NTOPNG_PRO
424
  if(ifRoles_shadow) delete ifRoles_shadow;
425
  if(ifRoles)        delete ifRoles;
426
#endif
427
4
}
428
429
/* ******************************************* */
430
431
4
void Ntop::registerPrefs(Prefs* _prefs, bool quick_registration) {
432
4
  char value[32];
433
4
  struct stat buf;
434
435
4
  prefs = _prefs;
436
437
4
  if (!quick_registration) {
438
4
    if (stat(prefs->get_data_dir(), &buf) ||
439
4
        (!(buf.st_mode & S_IFDIR)) /* It's not a directory */
440
        // || (!(buf.st_mode & S_IWRITE)) /* It's not writable    */
441
4
    ) {
442
0
      ntop->getTrace()->traceEvent(
443
0
          TRACE_ERROR, "Invalid directory %s specified", prefs->get_data_dir());
444
0
      exit(-1);
445
0
    }
446
447
4
    if (stat(prefs->get_callbacks_dir(), &buf) ||
448
4
        (!(buf.st_mode & S_IFDIR)) /* It's not a directory */
449
4
        || (!(buf.st_mode & S_IREAD)) /* It's not readable    */) {
450
0
      ntop->getTrace()->traceEvent(TRACE_ERROR,
451
0
                                   "Invalid directory %s specified",
452
0
                                   prefs->get_callbacks_dir());
453
0
      exit(-1);
454
0
    }
455
4
  }
456
457
4
  if (!quick_registration) {
458
    /* Initialize redis and populate some default values */
459
4
    Utils::initRedis(&redis, prefs->get_redis_host(),
460
4
                     prefs->get_redis_password(), prefs->get_redis_port(),
461
4
                     prefs->get_redis_db_id(), quick_registration,
462
4
                     prefs->get_redis_tls_ca_cert(), prefs->get_redis_tls_cert(),
463
4
                     prefs->get_redis_tls_key(), prefs->get_redis_tls_skip_verify());
464
4
    if (redis) redis->setDefaults();
465
4
  }
466
467
4
  if ((!quick_registration) && (!prefs->limitResourcesUsage())) {
468
    /* Initialize another redis instance for the trace of events */
469
4
    ntop->getTrace()->initRedis(
470
4
        prefs->get_redis_host(), prefs->get_redis_password(),
471
4
        prefs->get_redis_port(), prefs->get_redis_db_id(),
472
4
        prefs->get_redis_tls_ca_cert(), prefs->get_redis_tls_cert(),
473
4
        prefs->get_redis_tls_key(), prefs->get_redis_tls_skip_verify());
474
475
4
    if (ntop->getRedis() == NULL) {
476
0
      ntop->getTrace()->traceEvent(TRACE_ERROR,
477
0
                                   "Unable to initialize redis. Quitting...");
478
0
      exit(-1);
479
0
    }
480
4
  } else
481
0
    ntop->getTrace()->setRedis(getRedis());
482
483
#ifdef NTOPNG_PRO
484
  pro->initialize(quick_registration);
485
#endif
486
487
4
  if (quick_registration) return;
488
489
  /* Init local_network_max_id from Redis before calling addLocalNetwork() and assign new ID */
490
4
  initLocalNetworkMaxIdFromRedis();
491
492
4
  if (prefs->get_local_networks()) {
493
0
    setLocalNetworks(prefs->get_local_networks());
494
4
  } else {
495
    /* Add defaults */
496
    /* http://www.networksorcery.com/enp/protocol/ip/multicast.htm */
497
4
    setLocalNetworks((char*)CONST_DEFAULT_LOCAL_NETS);
498
4
  }
499
500
4
  checkReloadAlertExclusions();
501
502
4
  int num_resolvers =
503
4
      ntop->getPrefs()->limitResourcesUsage() ? 1 : CONST_NUM_RESOLVERS;
504
#if defined(NTOPNG_PRO) || defined(HAVE_NEDGE)
505
  if (pro->is_embedded_version()) num_resolvers = 1;
506
#endif
507
4
  address = new (std::nothrow) AddressResolution(num_resolvers);
508
509
4
  system_interface = new (std::nothrow)
510
4
      NetworkInterface(SYSTEM_INTERFACE_NAME, SYSTEM_INTERFACE_NAME);
511
512
  /* License check could have increased the number of interfaces available */
513
4
  resetNetworkInterfaces();
514
515
  /* Read the old last_stats_reset */
516
4
  if (ntop->getRedis()->get((char*)LAST_RESET_TIME, value, sizeof(value)) >= 0)
517
0
    last_stats_reset = atol(value);
518
519
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
520
  /* Now we can enable the periodic activities */
521
  pa = new (std::nothrow) PeriodicActivities();
522
#endif
523
524
4
  prefs->loadInstanceNameDefaults();
525
526
#ifdef HAVE_RADIUS
527
  if (!prefs->limitResourcesUsage())
528
    radiusAcc = new (std::nothrow) Radius();
529
  else
530
    radiusAcc = NULL;
531
#endif
532
533
#ifdef NTOPNG_PRO
534
  oidcAuth = new (std::nothrow) OIDCAuthenticator();
535
#endif
536
537
  /* Moved from the constructor to registerPrefs to avoid
538
   * loading nDPI when --help or --verify-license is used */
539
4
  initAllowedProtocolPresets();
540
541
4
  redis->setInitializationComplete();
542
4
}
543
544
/* ******************************************* */
545
546
4
void Ntop::resetNetworkInterfaces() {
547
4
  if (iface) delete[] iface;
548
549
4
  if ((iface = new (std::nothrow)
550
4
           NetworkInterface*[MAX_NUM_DEFINED_INTERFACES]()) == NULL)
551
0
    throw "Not enough memory";
552
553
4
  ntop->getTrace()->traceEvent(TRACE_INFO, "Interfaces Available: %u",
554
4
                               MAX_NUM_DEFINED_INTERFACES);
555
4
}
556
557
/* ******************************************* */
558
559
0
void Ntop::createExportInterface() {
560
0
#ifdef HAVE_ZMQ
561
0
#ifndef HAVE_NEDGE
562
0
  if (prefs->get_export_endpoint())
563
0
    export_interface =
564
0
        new (std::nothrow) ExportInterface(prefs->get_export_endpoint());
565
0
  else
566
0
    export_interface = NULL;
567
0
#endif
568
0
#endif
569
0
}
570
571
/* ******************************************* */
572
573
0
void Ntop::start() {
574
0
  struct timeval begin, end;
575
0
  u_long usec_diff;
576
0
  char daybuf[64], buf[128];
577
0
  time_t when = time(NULL);
578
0
  int i = 0;
579
580
0
  getTrace()->traceEvent(TRACE_NORMAL, "Welcome to %s %s v.%s (%s)",
581
#ifdef HAVE_NEDGE
582
                         "ntopng edge",
583
#else
584
0
                         "ntopng",
585
0
#endif
586
0
                         PACKAGE_MACHINE, PACKAGE_VERSION, NTOPNG_GIT_RELEASE);
587
588
0
  if (PACKAGE_OS[0] != '\0')
589
0
    getTrace()->traceEvent(TRACE_NORMAL, "Built on %s", PACKAGE_OS);
590
591
0
  getTrace()->traceEvent(TRACE_NORMAL, "(C) 1998-26 ntop");
592
593
0
  last_modified_static_file_epoch = start_time = time(NULL);
594
0
  snprintf(epoch_buf, sizeof(epoch_buf), "%u", (u_int32_t)start_time);
595
596
0
  string_to_replace[i].key = CONST_HTTP_PREFIX_STRING,
597
0
  string_to_replace[i].val = ntop->getPrefs()->get_http_prefix();
598
0
  i++;
599
0
  string_to_replace[i].key = CONST_NTOP_STARTUP_EPOCH,
600
0
  string_to_replace[i].val = ntop->getStartTimeString();
601
0
  i++;
602
0
  string_to_replace[i].key = CONST_NTOP_PRODUCT_NAME,
603
0
  string_to_replace[i].val =
604
#ifdef HAVE_NEDGE
605
      ntop->getPro()->get_product_name()
606
#else
607
0
      (char*)"ntopng"
608
0
#endif
609
0
      ;
610
0
  i++;
611
0
  string_to_replace[i].key = NULL, string_to_replace[i].val = NULL;
612
613
0
  strftime(daybuf, sizeof(daybuf), CONST_DB_DAY_FORMAT, localtime(&when));
614
0
  snprintf(buf, sizeof(buf), "ntopng.%s.hostkeys", daybuf);
615
616
#ifdef NTOPNG_PRO
617
  pro->printLicenseInfo();
618
#endif
619
620
0
  FlowRiskAlerts::checkUndefinedRisks();
621
622
0
  loadLocalInterfaceAddress();
623
624
0
  address->startResolveAddressLoop();
625
626
0
  system_interface->allocateStructures();
627
628
0
  for (int i = 0; i < num_defined_interfaces; i++)
629
0
    iface[i]->allocateStructures();
630
631
  /* Note: must start periodic activities loop only *after* interfaces have been
632
   * completely initialized.
633
   *
634
   * Note: this will also run the startup.lua script sequentially.
635
   * After this call, startup.lua has completed. */
636
0
  pa->startPeriodicActivitiesLoop();
637
638
0
  if (get_HTTPserver()) get_HTTPserver()->start_accepting_requests();
639
640
#ifdef HAVE_NEDGE
641
  /* TODO: enable start/stop of the captive portal webserver directly from Lua
642
   */
643
  if (get_HTTPserver() && prefs->isCaptivePortalEnabled())
644
    get_HTTPserver()->startCaptiveServer();
645
#endif
646
647
#ifdef HAVE_NEDGE
648
  char key[128];
649
  char** keys = NULL;
650
  int nkeys;
651
  char repeater[256];
652
653
  snprintf(key, sizeof(key), "ntopng.prefs.config.repeater.*");
654
655
  nkeys = redis->keys(key, &keys);
656
657
  for (int i = 0; i < nkeys; i++) {
658
    memset(repeater, 0, sizeof(repeater));
659
    redis->get(keys[i], repeater, sizeof(repeater));
660
661
    string ip;
662
    int port = 0;
663
    string interfaces;
664
    string restricted_interfaces;
665
    string type;
666
    bool keep_source = false;
667
    char* tmp = NULL;
668
669
    char* token = strtok_r(repeater, "|", &tmp);
670
    if (token != NULL) {
671
      type = token;
672
      token = strtok_r(NULL, "|", &tmp);
673
    }
674
    if (token != NULL) {
675
      ip = token;
676
      token = strtok_r(NULL, "|", &tmp);
677
    }
678
    if (token != NULL) {
679
      port = atoi(token);
680
      token = strtok_r(NULL, "|", &tmp);
681
    }
682
    if (token != NULL) {
683
      interfaces = token;
684
      token = strtok_r(NULL, "|", &tmp);
685
    }
686
    if (token != NULL) {
687
      keep_source = (token[0] == '1');
688
      token = strtok_r(NULL, "|", &tmp);
689
    }
690
    if (token != NULL) {
691
      restricted_interfaces = token;
692
    }
693
    if (interfaces.length() > 0) {
694
      PacketForwarder* multicastForwarder;
695
696
      if (ip.length() > 3 && strcmp(ip.c_str() + ip.length() - 3, "255") == 0)
697
        multicastForwarder = new (std::nothrow) BroadcastForwarder(
698
            ip, port, interfaces, restricted_interfaces, keep_source);
699
      else
700
        multicastForwarder = new (std::nothrow) MulticastForwarder(
701
            ip, port, interfaces, restricted_interfaces, keep_source);
702
703
      if (multicastForwarder) {
704
        multicastForwarder->start();
705
        multicastForwarders.push_back(multicastForwarder);
706
      } else {
707
        ntop->getTrace()->traceEvent(
708
            TRACE_ERROR,
709
            "Error occured instantiating forwarder on IP %s Port %d",
710
            ip.c_str(), port);
711
      }
712
    }
713
714
    if (keys[i]) free(keys[i]);
715
  }
716
  if (keys) free(keys);
717
#endif
718
719
0
  checkReloadHostPools();
720
0
  checkReloadFlowChecks();
721
0
  checkReloadHostChecks();
722
723
0
  for (int i = 0; i < num_defined_interfaces; i++)
724
0
    iface[i]->startPacketPolling();
725
726
0
  for (int i = 0; i < num_defined_interfaces; i++)
727
0
    iface[i]->checkPointCounters(true); /* Reset drop counters */
728
729
  /* Align to the next 5-th second of the clock to make sure
730
     housekeeping starts alinged (and remains aligned when
731
     the housekeeping frequency is a multiple of 5 seconds) */
732
0
  gettimeofday(&begin, NULL);
733
0
  _usleep((5 - begin.tv_sec % 5) * 1e6 - begin.tv_usec);
734
735
0
  registerThread("ntopng", pthread_self());
736
737
0
  globals->setInitialized(); /* We're ready to go */
738
739
0
  while ((!globals->isShutdown()) && (!globals->isShutdownRequested())) {
740
0
    const u_int32_t nap_usec =
741
0
        ntop->getPrefs()->get_housekeeping_frequency() * 1e6; /* 5 sec */
742
743
0
    gettimeofday(&begin, NULL);
744
745
    /* Run periodic tasks (note: this also runs runHousekeepingTasks) */
746
0
    runPeriodicHousekeepingTasks();
747
748
    /*
749
      Check if it is time to signal the shutdown, depending on the
750
      configuration. NOTE: Shutdown when done is only meaningful for pcap-dump
751
      interfaces when the file has been read.
752
    */
753
0
    checkShutdownWhenDone();
754
755
0
    gettimeofday(&end, NULL);
756
0
    usec_diff = Utils::usecTimevalDiff(&end, &begin);
757
758
0
    if (usec_diff >= nap_usec) {
759
0
      ntop->getTrace()->traceEvent(TRACE_INFO, "Housekeeping activities (main loop) took %.3fs",
760
0
           (float)usec_diff / 1e6);
761
0
    } else {
762
0
      while (usec_diff < nap_usec) {
763
0
        u_int32_t remaining_usec = nap_usec - usec_diff;
764
0
        u_int32_t power_nap_usec = 100 * 1e3; /* 100 msec */
765
766
0
        _usleep(remaining_usec >= power_nap_usec ? power_nap_usec
767
0
                                                 : remaining_usec);
768
769
        /* Run high frequency tasks */
770
0
        runHousekeepingTasks();
771
772
0
        gettimeofday(&end, NULL);
773
0
        usec_diff = Utils::usecTimevalDiff(&end, &begin);
774
0
      }
775
0
    }
776
0
  }
777
0
}
778
779
/* ******************************************* */
780
781
bool Ntop::isLocalAddress(int family, void* addr, int32_t* network_id,
782
104k
                          u_int8_t* network_mask_bits) {
783
104k
  u_int8_t nmask_bits;
784
#if 0
785
  char ipb[32];
786
787
  ntop->getTrace()->traceEvent(TRACE_NORMAL, "Find %s",
788
    Utils::intoaV4(ntohl(*(u_int32_t*)addr), ipb, sizeof(ipb)));
789
#endif
790
791
104k
  *network_id = localNetworkLookup(family, addr, &nmask_bits);
792
793
104k
  if ((*network_id != -1) && network_mask_bits)
794
0
    *network_mask_bits = nmask_bits;
795
  
796
104k
  return (((*network_id) == -1) ? false : true);
797
104k
};
798
799
/* ******************************************* */
800
801
void Ntop::getLocalNetworkIp(int32_t local_network_id, IpAddress** network_ip,
802
0
                             u_int8_t* network_prefix) {
803
0
  char *network_address, *slash;
804
805
0
  *network_ip = NULL;
806
0
  *network_prefix = 0;
807
808
0
  if (local_network_id >= 0) {
809
0
    const char* name = getLocalNetworkName(local_network_id);
810
0
    if (!name) return;
811
0
    network_address = strdup(name);
812
0
  } else
813
0
    network_address = strdup((char*)"0.0.0.0/0"); /* Remote networks */
814
815
0
  if ((slash = strchr(network_address, '/'))) {
816
0
    *network_prefix = atoi(slash + 1);
817
0
    *slash = '\0';
818
0
  }
819
820
0
  *network_ip = new (std::nothrow) IpAddress();
821
0
  if (*network_ip) (*network_ip)->set(network_address);
822
823
0
  if (network_address) free(network_address);
824
0
};
825
826
/* ******************************************* */
827
828
#ifdef WIN32
829
830
#include <ws2tcpip.h>
831
#include <iphlpapi.h>
832
833
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
834
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
835
836
/* Note: could also use malloc() and free() */
837
838
char* Ntop::getIfName(int if_id, char* name, u_int name_len) {
839
  // Declare and initialize variables
840
  PIP_INTERFACE_INFO pInfo = NULL;
841
  ULONG ulOutBufLen = 0;
842
  DWORD dwRetVal = 0;
843
  int iReturn = 1;
844
  int i;
845
846
  name[0] = '\0';
847
848
  // Make an initial call to GetInterfaceInfo to get
849
  // the necessary size in the ulOutBufLen variable
850
  dwRetVal = GetInterfaceInfo(NULL, &ulOutBufLen);
851
  if (dwRetVal == ERROR_INSUFFICIENT_BUFFER) {
852
    pInfo = (IP_INTERFACE_INFO*)MALLOC(ulOutBufLen);
853
    if (pInfo == NULL) {
854
      return (name);
855
    }
856
  }
857
858
  // Make a second call to GetInterfaceInfo to get
859
  // the actual data we need
860
  dwRetVal = GetInterfaceInfo(pInfo, &ulOutBufLen);
861
  if (dwRetVal == NO_ERROR) {
862
    for (i = 0; i < pInfo->NumAdapters; i++) {
863
      if (pInfo->Adapter[i].Index == if_id) {
864
        int j, k, begin = 0;
865
866
        for (j = 0, k = 0;
867
             (k < name_len) && (pInfo->Adapter[i].Name[j] != '\0'); j++) {
868
          if (begin) {
869
            if ((char)pInfo->Adapter[i].Name[j] == '}') break;
870
            name[k++] = (char)pInfo->Adapter[i].Name[j];
871
          } else if ((char)pInfo->Adapter[i].Name[j] == '{')
872
            begin = 1;
873
        }
874
875
        name[k] = '\0';
876
      }
877
      break;
878
    }
879
  }
880
881
  FREE(pInfo);
882
  return (name);
883
}
884
885
#endif
886
887
/* ******************************************* */
888
889
/* ******************************************* */
890
891
0
void Ntop::loadLocalInterfaceAddress() {
892
0
  const int bufsize = 128;
893
0
  char buf[bufsize];
894
895
#ifdef WIN32
896
  PMIB_IPADDRTABLE pIPAddrTable;
897
  DWORD dwSize = 0;
898
  DWORD dwRetVal = 0;
899
  IN_ADDR IPAddr;
900
  char buf2[bufsize];
901
902
  /* Variables used to return error message */
903
  LPVOID lpMsgBuf;
904
905
  // Before calling AddIPAddress we use GetIpAddrTable to get
906
  // an adapter to which we can add the IP.
907
  pIPAddrTable = (MIB_IPADDRTABLE*)MALLOC(sizeof(MIB_IPADDRTABLE));
908
909
  if (pIPAddrTable) {
910
    // Make an initial call to GetIpAddrTable to get the
911
    // necessary size into the dwSize variable
912
    if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
913
      FREE(pIPAddrTable);
914
      pIPAddrTable = (MIB_IPADDRTABLE*)MALLOC(dwSize);
915
    }
916
    if (pIPAddrTable == NULL) {
917
      return;
918
    }
919
  }
920
921
  // Make a second call to GetIpAddrTable to get the
922
  // actual data we want
923
  if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) {
924
    if (FormatMessage(
925
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
926
                FORMAT_MESSAGE_IGNORE_INSERTS,
927
            NULL, dwRetVal,
928
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // Default language
929
            (LPTSTR)&lpMsgBuf, 0, NULL)) {
930
      LocalFree(lpMsgBuf);
931
    }
932
933
    return;
934
  }
935
936
  for (int ifIdx = 0; ifIdx < (int)pIPAddrTable->dwNumEntries; ifIdx++) {
937
    char name[256];
938
939
    getIfName(pIPAddrTable->table[ifIdx].dwIndex, name, sizeof(name));
940
941
    for (int id = 0; id < num_defined_interfaces; id++) {
942
      if ((name[0] != '\0') && (strstr(iface[id]->get_name(), name) != NULL)) {
943
        u_int32_t bits = Utils::numberOfSetBits(
944
            (u_int32_t)pIPAddrTable->table[ifIdx].dwMask);
945
946
        IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[ifIdx].dwAddr;
947
        snprintf(buf, bufsize, "%s/32", inet_ntoa(IPAddr));
948
        local_interface_addresses.addAddress(buf);
949
        ntop->getTrace()->traceEvent(TRACE_NORMAL,
950
                                     "Adding %s as IPv4 NIC addr. [%s]", buf,
951
                                     iface[id]->get_description());
952
        iface[id]->addInterfaceAddress(buf);
953
954
        IPAddr.S_un.S_addr = (u_long)(pIPAddrTable->table[ifIdx].dwAddr &
955
                                      pIPAddrTable->table[ifIdx].dwMask);
956
        snprintf(buf2, bufsize, "%s/%u", inet_ntoa(IPAddr), bits);
957
        ntop->getTrace()->traceEvent(TRACE_NORMAL,
958
                                     "Adding %s as IPv4 local nw [%s]", buf2,
959
                                     iface[id]->get_description());
960
        addLocalNetworkList(buf2);
961
        iface[id]->addInterfaceNetwork(buf2, buf);
962
      }
963
    }
964
  }
965
966
  /* TODO: add IPv6 support */
967
  if (pIPAddrTable) {
968
    FREE(pIPAddrTable);
969
    pIPAddrTable = NULL;
970
  }
971
#else
972
0
  struct ifaddrs *local_addresses, *ifa;
973
  /* buf must be big enough for an IPv6 address(e.g.
974
   * 3ffe:2fa0:1010:ca22:020a:95ff:fe8a:1cf8) */
975
0
  char buf_orig[bufsize + 32];
976
0
  char net_buf[bufsize + 32];
977
0
  int sock =
978
0
      Utils::openSocket(AF_INET, SOCK_STREAM, 0, "loadLocalInterfaceAddress");
979
0
  std::map<std::string, int> ifaces;
980
0
  std::map<std::string, int>::iterator it;
981
982
0
  for (int i = 0; i < num_defined_interfaces; i++) {
983
0
    char ifbuf[256], *tok, *tmp;
984
985
0
    snprintf(ifbuf, sizeof(ifbuf), "%s", iface[i]->get_name());
986
0
    tok = strtok_r(ifbuf, ",", &tmp);
987
988
0
    while (tok != NULL) {
989
0
      if (strchr(tok, ':') == NULL) ifaces[std::string(tok)] = i;
990
991
0
      tok = strtok_r(NULL, ",", &tmp);
992
0
    }
993
0
  }
994
995
0
  if (getifaddrs(&local_addresses) != 0) {
996
0
    ntop->getTrace()->traceEvent(TRACE_ERROR,
997
0
                                 "Unable to read interface addresses");
998
0
    Utils::closeSocket(sock);
999
0
    return;
1000
0
  }
1001
1002
0
  for (ifa = local_addresses; ifa != NULL; ifa = ifa->ifa_next) {
1003
0
    struct ifreq ifr;
1004
0
    u_int32_t netmask;
1005
0
    int cidr, ifId = -1;
1006
1007
0
    if ((ifa->ifa_addr == NULL) ||
1008
0
        ((ifa->ifa_addr->sa_family != AF_INET) &&
1009
0
         (ifa->ifa_addr->sa_family != AF_INET6)) ||
1010
0
        ((ifa->ifa_flags & IFF_UP) == 0))
1011
0
      continue;
1012
1013
0
    it = ifaces.find(ifa->ifa_name);
1014
0
    if (it != ifaces.end())
1015
0
      ifId = it->second;
1016
0
    else
1017
0
      continue;
1018
1019
0
    if (ifa->ifa_addr->sa_family == AF_INET) {
1020
0
      struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
1021
0
      u_int32_t nm;
1022
1023
0
      memset(&ifr, 0, sizeof(ifr));
1024
0
      ifr.ifr_addr.sa_family = AF_INET;
1025
0
      strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name) - 1);
1026
0
      ioctl(sock, SIOCGIFNETMASK, &ifr);
1027
0
      netmask = ((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr.s_addr;
1028
1029
0
      cidr = 0, nm = netmask;
1030
1031
0
      while (nm) {
1032
0
        cidr += (nm & 0x01);
1033
0
        nm >>= 1;
1034
0
      }
1035
1036
0
      if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), buf,
1037
0
                    sizeof(buf)) != NULL) {
1038
0
        char buf_orig2[bufsize + 32];
1039
1040
0
        snprintf(buf_orig2, sizeof(buf_orig2), "%s/%d", buf, 32);
1041
0
        ntop->getTrace()->traceEvent(
1042
0
            TRACE_NORMAL, "Adding %s as IPv4 interface address for %s",
1043
0
            buf_orig2, iface[ifId]->get_name());
1044
0
        local_interface_addresses.addAddress(buf_orig2);
1045
0
        iface[ifId]->addInterfaceAddress(buf_orig2);
1046
1047
        /* Set to zero non network bits */
1048
0
        s4->sin_addr.s_addr =
1049
0
            htonl(ntohl(s4->sin_addr.s_addr) & ntohl(netmask));
1050
0
        inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), buf,
1051
0
                  sizeof(buf));
1052
0
        snprintf(net_buf, sizeof(net_buf), "%s/%d", buf, cidr);
1053
0
        ntop->getTrace()->traceEvent(TRACE_NORMAL,
1054
0
                                     "Adding %s as IPv4 local network for %s",
1055
0
                                     net_buf, iface[ifId]->get_name());
1056
0
        iface[ifId]->addInterfaceNetwork(net_buf, buf_orig2);
1057
0
        addLocalNetworkList(net_buf);
1058
0
      }
1059
0
    } else if (ifa->ifa_addr->sa_family == AF_INET6) {
1060
0
      struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_netmask);
1061
0
      u_int8_t* b = (u_int8_t*)&(s6->sin6_addr);
1062
1063
0
      cidr = 0;
1064
1065
0
      for (int i = 0; i < 16; i++) {
1066
0
        u_int8_t num_bits = __builtin_popcount(b[i]);
1067
1068
0
        if (num_bits == 0) break;
1069
0
        cidr += num_bits;
1070
0
      }
1071
1072
0
      s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
1073
0
      if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), buf,
1074
0
                    sizeof(buf)) != NULL) {
1075
0
        snprintf(buf_orig, sizeof(buf_orig), "%s/%d", buf, 128);
1076
1077
0
        ntop->getTrace()->traceEvent(
1078
0
            TRACE_NORMAL, "Adding %s as IPv6 interface address for %s",
1079
0
            buf_orig, iface[ifId]->get_name());
1080
0
        local_interface_addresses.addAddresses(buf_orig);
1081
0
        iface[ifId]->addInterfaceAddress(buf_orig);
1082
1083
0
        for (int i = cidr, j = 0; i > 0; i -= 8, ++j)
1084
0
          s6->sin6_addr.s6_addr[j] &=
1085
0
              i >= 8 ? 0xff : (u_int32_t)((0xffU << (8 - i)) & 0xffU);
1086
1087
0
        inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), buf,
1088
0
                  sizeof(buf));
1089
0
        snprintf(net_buf, sizeof(net_buf), "%s/%d", buf, cidr);
1090
0
        ntop->getTrace()->traceEvent(TRACE_NORMAL,
1091
0
                                     "Adding %s as IPv6 local network for %s",
1092
0
                                     net_buf, iface[ifId]->get_name());
1093
1094
0
        iface[ifId]->addInterfaceNetwork(net_buf, buf_orig);
1095
0
        addLocalNetworkList(net_buf);
1096
0
      }
1097
0
    }
1098
0
  }
1099
1100
0
  freeifaddrs(local_addresses);
1101
1102
0
  Utils::closeSocket(sock);
1103
0
#endif
1104
1105
0
  ntop->getTrace()->traceEvent(TRACE_INFO,
1106
0
                               "Local Interface Addresses (System Host)");
1107
0
  ntop->getTrace()->traceEvent(TRACE_INFO, "Local Networks");
1108
0
}
1109
1110
/* ******************************************* */
1111
1112
4
void Ntop::loadGeolocation() {
1113
4
  if (geo != NULL) delete geo;
1114
4
  geo = new (std::nothrow) Geolocation();
1115
4
}
1116
1117
/* ******************************************* */
1118
1119
0
void Ntop::loadMacManufacturers(char* dir) {
1120
0
  if (mac_manufacturers != NULL) delete mac_manufacturers;
1121
0
  if ((mac_manufacturers = new (std::nothrow) MacManufacturers(dir)) == NULL)
1122
0
    throw "Not enough memory";
1123
0
}
1124
1125
/* ******************************************* */
1126
1127
0
bool Ntop::startPollingBGPPrefixChanges(char *zmq_url) {
1128
0
  char buf[128];
1129
  
1130
0
  if((zmq_url == NULL) && (redis != NULL)) {
1131
    /* In case zmq_url is not specified, we read it from preferences */
1132
0
    if(redis->get((char*)CONST_BGP_PREFIX_ENDPOINT_NAME, buf, sizeof(buf)) == 0)
1133
0
      zmq_url = buf;
1134
0
  }
1135
1136
0
  if((zmq_url == NULL) || (zmq_url[0] == '\0') )
1137
0
    return(false); /* Nothing to do */
1138
  
1139
0
  if(bgp != NULL) {
1140
    /* Terminate an existing BGP if existing */
1141
0
    delete bgp;
1142
0
    bgp = NULL;
1143
0
  }
1144
1145
0
  bgp = new BGPPrefixListener(zmq_url);
1146
1147
0
  return(true);
1148
0
}
1149
1150
/* ******************************************* */
1151
1152
#ifdef HAVE_SNMP_TRAP
1153
void Ntop::initSNMPTrapCollector() {
1154
  if (trap_collector != NULL) return; /* already initialized */
1155
1156
  ntop->getTrace()->traceEvent(TRACE_NORMAL,
1157
                               "Initializing SNMP Trap collector");
1158
1159
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(WIN32) && \
1160
    !defined(HAVE_NEDGE)
1161
  if (Utils::gainWriteCapabilities() == -1)
1162
    ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to enable capabilities");
1163
#endif
1164
1165
  try {
1166
    trap_collector = new SNMPTrap();
1167
  } catch (...) {
1168
    ntop->getTrace()->traceEvent(TRACE_ERROR,
1169
                                 "Unable to initialize SNMP traps collector");
1170
  }
1171
1172
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(WIN32) && \
1173
    !defined(HAVE_NEDGE)
1174
  Utils::dropWriteCapabilities();
1175
#endif
1176
}
1177
1178
/* ******************************************* */
1179
1180
void Ntop::toggleSNMPTrapCollector(bool enable) {
1181
  if (trap_collector == NULL) {
1182
    initSNMPTrapCollector();
1183
1184
    if (trap_collector == NULL) return;
1185
  }
1186
1187
  if (enable) {
1188
    trap_collector->startTrapCollection();
1189
  } else {
1190
    trap_collector->stopTrapCollection();
1191
  }
1192
}
1193
#endif
1194
1195
/* ******************************************* */
1196
1197
4
void Ntop::setWorkingDir(char* dir) {
1198
4
  snprintf(working_dir, sizeof(working_dir), "%s", dir);
1199
4
  removeTrailingSlash(working_dir);
1200
4
  setScriptsDir();
1201
4
};
1202
1203
/* ******************************************* */
1204
1205
24
void Ntop::removeTrailingSlash(char* str) {
1206
24
  int len = (int)strlen(str) - 1;
1207
1208
24
  if ((len > 0) && ((str[len] == '/') || (str[len] == '\\'))) str[len] = '\0';
1209
24
}
1210
1211
/* ******************************************* */
1212
1213
0
void Ntop::setCustomnDPIProtos(char* path) {
1214
0
  if (path != NULL) {
1215
0
    if (custom_ndpi_protos != NULL) free(custom_ndpi_protos);
1216
0
    custom_ndpi_protos = strdup(path);
1217
0
  }
1218
0
}
1219
1220
/* ******************************************* */
1221
1222
void Ntop::lua_periodic_activities_stats(NetworkInterface* iface,
1223
0
                                         lua_State* vm) {
1224
0
  if (pa) pa->lua(iface, vm);
1225
0
}
1226
1227
/* ******************************************* */
1228
1229
0
void Ntop::lua_alert_queues_stats(lua_State* vm) {
1230
0
  lua_newtable(vm);
1231
1232
0
  if (getInternalAlertsQueue())
1233
0
    getInternalAlertsQueue()->lua(vm, "internal_alerts_queue");
1234
1235
0
  lua_pushstring(vm, "alert_queues");
1236
0
  lua_insert(vm, -2);
1237
0
  lua_settable(vm, -3);
1238
0
}
1239
1240
/* ******************************************* */
1241
1242
0
bool Ntop::recipients_are_empty() { return recipients.empty(); }
1243
1244
/* ******************************************* */
1245
1246
0
bool Ntop::recipients_enqueue(AlertFifoItem* notification) {
1247
0
  return recipients.enqueue(notification);
1248
0
}
1249
1250
/* ******************************************* */
1251
1252
bool Ntop::recipient_enqueue(u_int16_t recipient_id,
1253
0
                             const AlertFifoItem* const notification) {
1254
0
  return recipients.enqueue(recipient_id, notification);
1255
0
}
1256
1257
/* ******************************************* */
1258
1259
0
AlertFifoItem* Ntop::recipient_dequeue(u_int16_t recipient_id) {
1260
0
  return recipients.dequeue(recipient_id);
1261
0
}
1262
1263
/* ******************************************* */
1264
1265
0
void Ntop::recipient_stats(u_int16_t recipient_id, lua_State* vm) {
1266
0
  recipients.lua(recipient_id, vm);
1267
0
}
1268
1269
/* ******************************************* */
1270
1271
0
time_t Ntop::recipient_last_use(u_int16_t recipient_id) {
1272
0
  return recipients.last_use(recipient_id);
1273
0
}
1274
1275
/* ******************************************* */
1276
1277
void Ntop::inc_recipient_stats(u_int16_t recipient_id, u_int64_t delivered,
1278
                               u_int64_t filtered_out,
1279
0
                               u_int64_t delivery_failures) {
1280
0
  recipients.incStats(recipient_id, delivered, filtered_out, delivery_failures);
1281
0
}
1282
1283
/* ******************************************* */
1284
1285
0
void Ntop::recipient_delete(u_int16_t recipient_id) {
1286
0
  recipients.delete_recipient(recipient_id);
1287
0
}
1288
1289
/* ******************************************* */
1290
1291
void Ntop::recipient_register(
1292
    u_int16_t recipient_id, AlertLevel minimum_severity,
1293
    Bitmap128 enabled_categories, Bitmap4096 enabled_host_pools,
1294
    Bitmap128 enabled_entities, Bitmap128 enabled_flow_alert_types,
1295
    Bitmap128 enabled_host_alert_types, Bitmap128 enabled_other_alert_types,
1296
0
    bool match_alert_id, bool skip_alerts, Bitmap64 enabled_tags) {
1297
0
  recipients.register_recipient(
1298
0
      recipient_id, minimum_severity, enabled_categories, enabled_host_pools,
1299
0
      enabled_entities, enabled_flow_alert_types, enabled_host_alert_types,
1300
0
      enabled_other_alert_types, match_alert_id, skip_alerts, enabled_tags);
1301
0
}
1302
1303
/* ******************************************* */
1304
1305
0
AlertLevel Ntop::get_default_recipient_minimum_severity() {
1306
0
  return recipients.get_default_recipient_minimum_severity();
1307
0
}
1308
1309
/* ******************************************* */
1310
1311
0
void Ntop::getUsers(lua_State* vm) {
1312
0
  char** usernames;
1313
0
  char* username;
1314
0
  char *key, *val;
1315
0
  int rc, i;
1316
0
  size_t len;
1317
1318
0
  lua_newtable(vm);
1319
1320
0
  if ((rc = ntop->getRedis()->keys("ntopng.user.*.password", &usernames)) <= 0)
1321
0
    return;
1322
1323
0
  if ((key = (char*)malloc(CONST_MAX_LEN_REDIS_VALUE)) == NULL)
1324
0
    return;
1325
0
  else if ((val = (char*)malloc(CONST_MAX_LEN_REDIS_VALUE)) == NULL) {
1326
0
    free(key);
1327
0
    return;
1328
0
  }
1329
1330
0
  for (i = 0; i < rc; i++) {
1331
0
    if (usernames[i] == NULL) goto next_username; /* safety check */
1332
0
    if ((username = strchr(usernames[i], '.')) == NULL) goto next_username;
1333
0
    if ((username = strchr(username + 1, '.')) == NULL) goto next_username;
1334
0
    len = strlen(++username);
1335
1336
0
    if (len < sizeof(".password")) goto next_username;
1337
0
    username[len - sizeof(".password") + 1] = '\0';
1338
1339
0
    lua_newtable(vm);
1340
1341
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_FULL_NAME,
1342
0
             username);
1343
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1344
0
      lua_push_str_table_entry(vm, "full_name", val);
1345
0
    else
1346
0
      lua_push_str_table_entry(vm, "full_name", (char*)"unknown");
1347
1348
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_PASSWORD, username);
1349
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1350
0
      lua_push_str_table_entry(vm, "password", val);
1351
0
    else
1352
0
      lua_push_str_table_entry(vm, "password", (char*)"unknown");
1353
1354
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_GROUP, username);
1355
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1356
0
      lua_push_str_table_entry(vm, "group", val);
1357
0
    else
1358
0
      lua_push_str_table_entry(vm, "group", (char*)NTOP_UNKNOWN_GROUP);
1359
1360
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_LANGUAGE, username);
1361
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1362
0
      lua_push_str_table_entry(vm, "language", val);
1363
0
    else
1364
0
      lua_push_str_table_entry(vm, "language", (char*)"");
1365
1366
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOW_PCAP,
1367
0
             username);
1368
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1369
0
      lua_push_bool_table_entry(vm, "allow_pcap_download", true);
1370
1371
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE,
1372
0
             CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
1373
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1374
0
      lua_push_bool_table_entry(vm, "allow_historical_flows", true);
1375
1376
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOW_ALERTS,
1377
0
             username);
1378
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1379
0
      lua_push_bool_table_entry(vm, "allow_alerts", true);
1380
1381
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_NETS, username);
1382
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1383
0
      lua_push_str_table_entry(vm, CONST_ALLOWED_NETS, val);
1384
0
    else
1385
0
      lua_push_str_table_entry(vm, CONST_ALLOWED_NETS, (char*)"");
1386
1387
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOWED_IFNAME,
1388
0
             username);
1389
0
    if ((ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0) &&
1390
0
        val[0] != '\0')
1391
0
      lua_push_str_table_entry(vm, CONST_ALLOWED_IFNAME, val);
1392
0
    else
1393
0
      lua_push_str_table_entry(vm, CONST_ALLOWED_IFNAME, (char*)"");
1394
1395
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_HOST_POOL_ID,
1396
0
             username);
1397
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
1398
0
      lua_push_uint64_table_entry(vm, "host_pool_id", atoi(val));
1399
1400
0
    snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOWED_HOST_POOLS,
1401
0
             username);
1402
0
    if (ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0 &&
1403
0
        val[0] != '\0')
1404
0
      lua_push_str_table_entry(vm, "allowed_host_pools", val);
1405
0
    else
1406
0
      lua_push_str_table_entry(vm, "allowed_host_pools", (char*)"");
1407
1408
0
    lua_pushstring(vm, username);
1409
0
    lua_insert(vm, -2);
1410
0
    lua_settable(vm, -3);
1411
1412
0
  next_username:
1413
1414
0
    if (usernames[i]) free(usernames[i]);
1415
0
  }
1416
1417
0
  free(usernames);
1418
0
  free(key), free(val);
1419
0
}
1420
1421
/* ******************************************* */
1422
1423
/**
1424
 * @brief Check if the current user is an administrator
1425
 *
1426
 * @param vm   The lua state.
1427
 * @return true if the current user is an administrator, false otherwise.
1428
 */
1429
0
bool Ntop::isUserAdministrator(lua_State* vm) {
1430
0
  struct mg_connection* conn;
1431
0
  char *username, *group;
1432
1433
0
  if (!ntop->getPrefs()->is_users_login_enabled())
1434
0
    return (true); /* login disabled for all users, everyone's an admin */
1435
1436
0
  if ((conn = getLuaVMUservalue(vm, conn)) == NULL) {
1437
    /* this is an internal script (e.g. periodic script), admin check should
1438
     * pass */
1439
0
    return (true);
1440
0
  } else if (HTTPserver::authorized_localhost_user_login(conn))
1441
0
    return (true); /* login disabled from localhost, everyone's connecting from
1442
                      localhost is an admin */
1443
1444
0
  if ((username = getLuaVMUserdata(vm, user)) == NULL) {
1445
    // ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%s): NO", __FUNCTION__,
1446
    // "???");
1447
0
    return (false); /* Unknown */
1448
0
  }
1449
1450
0
  if (!strncmp(username, NTOP_NOLOGIN_USER, strlen(username))) return (true);
1451
1452
0
  if ((group = getLuaVMUserdata(vm, group)) != NULL) {
1453
0
    return (!strcmp(group, NTOP_NOLOGIN_USER) ||
1454
0
            !strcmp(group, CONST_ADMINISTRATOR_USER));
1455
0
  } else {
1456
    // ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%s): NO", __FUNCTION__,
1457
    // username);
1458
0
    return (false); /* Unknown */
1459
0
  }
1460
0
}
1461
1462
/* ******************************************* */
1463
1464
0
void Ntop::getAllowedInterface(lua_State* vm) {
1465
0
  char* allowed_ifname;
1466
1467
0
  allowed_ifname = getLuaVMUserdata(vm, allowed_ifname);
1468
1469
0
  lua_pushstring(vm, allowed_ifname != NULL ? allowed_ifname : (char*)"");
1470
0
}
1471
1472
/* ******************************************* */
1473
1474
0
void Ntop::getAllowedNetworks(lua_State* vm) {
1475
0
  char key[64], val[64];
1476
0
  const char* username = getLuaVMUservalue(vm, user);
1477
1478
0
  snprintf(key, sizeof(key), CONST_STR_USER_NETS, username ? username : "");
1479
0
  lua_pushstring(vm, (ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
1480
0
                         ? val
1481
0
                         : CONST_DEFAULT_ALL_NETS);
1482
0
}
1483
1484
/* ******************************************* */
1485
1486
// NOTE: ifname must be of size MAX_INTERFACE_NAME_LEN
1487
0
bool Ntop::getInterfaceAllowed(lua_State* vm, char* ifname) const {
1488
0
  char* allowed_ifname;
1489
1490
0
  allowed_ifname = getLuaVMUserdata(vm, allowed_ifname);
1491
1492
0
  if (ifname == NULL) return false;
1493
1494
0
  if ((allowed_ifname == NULL) || (allowed_ifname[0] == '\0')) {
1495
0
    ifname = NULL;
1496
0
    return false;
1497
0
  }
1498
1499
0
  strncpy(ifname, allowed_ifname, MAX_INTERFACE_NAME_LEN - 1);
1500
0
  ifname[MAX_INTERFACE_NAME_LEN - 1] = '\0';
1501
0
  return true;
1502
0
}
1503
1504
/* ******************************************* */
1505
1506
0
bool Ntop::isInterfaceAllowed(lua_State* vm, const char* ifname) const {
1507
0
  char* allowed_ifname;
1508
0
  bool ret;
1509
1510
0
  if (vm == NULL || ifname == NULL)
1511
0
    return true; /* Always return true when no lua state is passed */
1512
1513
0
  allowed_ifname = getLuaVMUserdata(vm, allowed_ifname);
1514
1515
0
  if ((allowed_ifname == NULL) || (allowed_ifname[0] == '\0')) {
1516
0
    ntop->getTrace()->traceEvent(TRACE_DEBUG,
1517
0
                                 "No allowed interface found for %s", ifname);
1518
    // this is a lua script called within ntopng (no HTTP UI and user
1519
    // interaction, e.g. startup.lua)
1520
0
    ret = true;
1521
0
  } else {
1522
0
    ntop->getTrace()->traceEvent(TRACE_DEBUG,
1523
0
                                 "Allowed interface %s, requested %s",
1524
0
                                 allowed_ifname, ifname);
1525
0
    ret = !strncmp(allowed_ifname, ifname, strlen(allowed_ifname));
1526
0
  }
1527
1528
0
  return ret;
1529
0
}
1530
1531
/* ******************************************* */
1532
1533
0
bool Ntop::isLocalUser(lua_State* vm) {
1534
0
  struct mg_connection* conn;
1535
1536
0
  if ((conn = getLuaVMUservalue(vm, conn)) == NULL) {
1537
    /* this is an internal script (e.g. periodic script), admin check should
1538
     * pass */
1539
0
    return (true);
1540
0
  }
1541
1542
0
  return getLuaVMUservalue(vm, localuser);
1543
0
}
1544
1545
/* ******************************************* */
1546
1547
0
bool Ntop::isInterfaceAllowed(lua_State* vm, int ifid) const {
1548
0
  return isInterfaceAllowed(vm, prefs->get_if_name(ifid));
1549
0
}
1550
1551
/* ******************************************* */
1552
1553
0
bool Ntop::isPcapDownloadAllowed(lua_State* vm, const char* ifname) {
1554
0
  bool allow_pcap_download = false;
1555
1556
0
  if (isUserAdministrator(vm)) return true;
1557
1558
0
  if (isInterfaceAllowed(vm, ifname)) {
1559
0
    char* username = getLuaVMUserdata(vm, user);
1560
0
    bool allow_historical_flows;
1561
0
    bool allow_alerts;
1562
1563
0
    ntop->getUserCapabilities(username, &allow_pcap_download,
1564
0
                              &allow_historical_flows, &allow_alerts);
1565
0
  }
1566
1567
0
  return (allow_pcap_download);
1568
0
}
1569
1570
/* ******************************************* */
1571
1572
0
char* Ntop::preparePcapDownloadFilter(lua_State* vm, char* filter) {
1573
0
  char* username;
1574
0
  char* restricted_filter = NULL;
1575
0
  char key[64], nets[MAX_USER_NETS_VAL_LEN], nets_cpy[MAX_USER_NETS_VAL_LEN];
1576
0
  char *tmp, *net;
1577
0
  int filter_len = 0, len = 0, off = 0, num_nets = 0;
1578
1579
  /* check user */
1580
1581
0
  if (isUserAdministrator(vm)) /* keep the original filter */
1582
0
    goto no_restriction;
1583
1584
0
  username = getLuaVMUserdata(vm, user);
1585
0
  if (username == NULL || username[0] == '\0') return (NULL);
1586
1587
  /* read networks */
1588
1589
0
  snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
1590
0
  if (ntop->getRedis()->get(key, nets, sizeof(nets)) < 0 || strlen(nets) == 0)
1591
0
    goto no_restriction; /* no subnet configured for this user */
1592
1593
0
  if (filter != NULL) filter_len = strlen(filter);
1594
1595
0
  num_nets = 0;
1596
0
  tmp = NULL;
1597
0
  strcpy(nets_cpy, nets);
1598
0
  net = strtok_r(nets_cpy, ",", &tmp);
1599
0
  while (net != NULL) {
1600
0
    if (strcmp(net, "0.0.0.0/0") != 0 && strcmp(net, "::/0") != 0) num_nets++;
1601
0
    net = strtok_r(NULL, ",", &tmp);
1602
0
  }
1603
1604
0
  if (num_nets == 0) goto no_restriction;
1605
1606
  /* build final/restricted filter */
1607
1608
0
  len = filter_len + strlen(nets) + num_nets * strlen(" or net ") +
1609
0
        strlen("() and ()") + 1;
1610
1611
0
  restricted_filter = (char*)malloc(len + 1);
1612
0
  if (restricted_filter == NULL) return (NULL);
1613
0
  restricted_filter[0] = '\0';
1614
1615
0
  if (filter_len > 0) off += snprintf(&restricted_filter[off], len - off, "(");
1616
1617
0
  num_nets = 0;
1618
0
  tmp = NULL;
1619
0
  strcpy(nets_cpy, nets);
1620
0
  net = strtok_r(nets_cpy, ",", &tmp);
1621
0
  while (net != NULL) {
1622
0
    if (strcmp(net, "0.0.0.0/0") != 0 && strcmp(net, "::/0") != 0) {
1623
0
      if (num_nets > 0)
1624
0
        off += snprintf(&restricted_filter[off], len - off, " or ");
1625
0
      off += snprintf(&restricted_filter[off], len - off, "net %s", net);
1626
0
      num_nets++;
1627
0
    }
1628
0
    net = strtok_r(NULL, ",", &tmp);
1629
0
  }
1630
1631
0
  if (filter_len > 0)
1632
0
    off += snprintf(&restricted_filter[off], len - off, ") and (%s)", filter);
1633
1634
0
  return (restricted_filter);
1635
1636
0
no_restriction:
1637
0
  return (strdup(filter == NULL ? "" : filter));
1638
0
}
1639
1640
/* ******************************************* */
1641
1642
0
bool Ntop::checkUserInterfaces(const char* user) const {
1643
0
  char ifbuf[MAX_INTERFACE_NAME_LEN];
1644
1645
  /* Check if the user has an allowed interface and that interface has not yet
1646
     been instantiated in ntopng (e.g, this can happen with dynamic interfaces
1647
     after ntopng has been restarted.) */
1648
0
  getUserAllowedIfname(user, ifbuf, sizeof(ifbuf));
1649
0
  if (ifbuf[0] != '\0' && !isExistingInterface(ifbuf)) return false;
1650
1651
0
  return true;
1652
0
}
1653
1654
/* ******************************************* */
1655
1656
bool Ntop::getUserPasswordHashLocal(const char* user, char* password_hash,
1657
0
                                    u_int password_hash_len) const {
1658
0
  char key[64], val[64];
1659
1660
0
  snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, user);
1661
1662
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) < 0) {
1663
0
    return (false);
1664
0
  }
1665
1666
0
  snprintf(password_hash, password_hash_len, "%s", val);
1667
0
  return (true);
1668
0
}
1669
1670
/* ******************************************* */
1671
1672
0
void Ntop::getUserGroupLocal(const char* user, char* group) const {
1673
0
  char key[64], val[64];
1674
1675
0
  snprintf(key, sizeof(key), CONST_STR_USER_GROUP, user);
1676
1677
0
  strncpy(group,
1678
0
          ((ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
1679
0
               ? val
1680
0
               : NTOP_UNKNOWN_GROUP),
1681
0
          NTOP_GROUP_MAXLEN - 1);
1682
0
  group[NTOP_GROUP_MAXLEN - 1] = '\0';
1683
0
}
1684
1685
/* ******************************************* */
1686
1687
0
bool Ntop::isLocalAuthEnabled() const {
1688
0
  char val[64];
1689
1690
0
  if ((ntop->getRedis()->get((char*)PREF_NTOP_LOCAL_AUTH, val, sizeof(val)) >=
1691
0
       0) &&
1692
0
      val[0] == '0')
1693
0
    return (false);
1694
1695
0
  return (true);
1696
0
}
1697
1698
/* ******************************************* */
1699
1700
bool Ntop::checkLocalAuth(const char* user, const char* password,
1701
0
                          char* group) const {
1702
0
  char val[64], password_hash[33];
1703
1704
0
  if (!isLocalAuthEnabled()) return (false);
1705
1706
0
  ntop->getTrace()->traceEvent(TRACE_INFO, "Checking Local auth");
1707
1708
0
  if ((!strcmp(user, "admin")) &&
1709
0
      (ntop->getRedis()->get((char*)TEMP_ADMIN_PASSWORD, val, sizeof(val)) >=
1710
0
       0) &&
1711
0
      (val[0] != '\0') && (!strcmp(val, password)))
1712
0
    goto valid_local_user;
1713
1714
0
  if (!getUserPasswordHashLocal(user, val, sizeof(val))) {
1715
0
    return (false);
1716
0
  } else {
1717
0
    mg_md5(password_hash, password, NULL);
1718
1719
0
    if (strcmp(password_hash, val) != 0) {
1720
0
      return (false);
1721
0
    }
1722
0
  }
1723
1724
0
valid_local_user:
1725
0
  getUserGroupLocal(user, group);
1726
1727
0
  return (true);
1728
0
}
1729
1730
/* ******************************************* */
1731
1732
bool Ntop::checkHTTPAuth(const char* user, const char* password,
1733
0
                         char* group) const {
1734
0
  int postLen;
1735
0
  char *httpUrl = NULL, *postData = NULL, *returnData = NULL;
1736
0
  bool http_ret = false;
1737
0
  int rc = 0;
1738
0
  HTTPTranferStats stats;
1739
0
  HTTPAuthenticator auth;
1740
0
  char val[64];
1741
1742
0
  if (ntop->getRedis()->get((char*)PREF_NTOP_HTTP_AUTH, val, sizeof(val)) < 0 ||
1743
0
      val[0] != '1')
1744
0
    return false;
1745
1746
0
  ntop->getTrace()->traceEvent(TRACE_INFO, "Checking HTTP auth");
1747
1748
0
  memset(&auth, 0, sizeof(auth));
1749
1750
0
  if (!password || !password[0]) return false;
1751
1752
  /* JSON-escape user and password to prevent injection via '"' or '\' in credentials.
1753
     user is bounded to 32 chars and password to 129, so 2x sizes cover worst case. */
1754
0
  {
1755
0
    char escaped_user[65] = {}, escaped_password[259] = {};
1756
0
    auto json_escape = [](const char* src, char* dst, size_t dst_size) {
1757
0
      size_t oi = 0;
1758
0
      for (size_t i = 0; src[i] && oi < dst_size - 2; i++) {
1759
0
        if (src[i] == '"' || src[i] == '\\') dst[oi++] = '\\';
1760
0
        dst[oi++] = src[i];
1761
0
      }
1762
0
      dst[oi] = '\0';
1763
0
    };
1764
0
    json_escape(user, escaped_user, sizeof(escaped_user));
1765
0
    json_escape(password, escaped_password, sizeof(escaped_password));
1766
0
    postLen = 100 + (int)strlen(escaped_user) + (int)strlen(escaped_password);
1767
0
    if (!(httpUrl = (char*)calloc(sizeof(char), MAX_HTTP_AUTHENTICATOR_LEN)) ||
1768
0
        !(postData = (char*)calloc(sizeof(char), postLen + 1)) ||
1769
0
        !(returnData = (char*)calloc(
1770
0
              sizeof(char), MAX_HTTP_AUTHENTICATOR_RETURN_DATA_LEN + 1))) {
1771
0
      ntop->getTrace()->traceEvent(TRACE_ERROR,
1772
0
                                   "HTTP: unable to allocate memory");
1773
0
      goto http_auth_out;
1774
0
    }
1775
0
    ntop->getRedis()->get((char*)PREF_HTTP_AUTHENTICATOR_URL, httpUrl,
1776
0
                          MAX_HTTP_AUTHENTICATOR_LEN);
1777
0
    if (!httpUrl[0]) {
1778
0
      ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: no http url set !");
1779
0
      goto http_auth_out;
1780
0
    }
1781
0
    snprintf(postData, postLen, "{\"user\": \"%s\", \"password\": \"%s\"}",
1782
0
             escaped_user, escaped_password);
1783
0
  }
1784
1785
0
  if (Utils::postHTTPJsonData(NULL,  // no token
1786
0
                              NULL,  // no digest user
1787
0
                              NULL,  // no digest password
1788
0
                              httpUrl, postData, 0, 0, &stats, returnData,
1789
0
                              MAX_HTTP_AUTHENTICATOR_RETURN_DATA_LEN, &rc)) {
1790
0
    if (rc == 200) {
1791
      // parse JSON
1792
0
      if (!Utils::parseAuthenticatorJson(&auth, returnData)) {
1793
0
        ntop->getTrace()->traceEvent(
1794
0
            TRACE_ERROR, "HTTP: unable to parse json answer data !");
1795
0
        goto http_auth_out;
1796
0
      }
1797
1798
0
      strncpy(
1799
0
          group,
1800
0
          auth.admin ? CONST_USER_GROUP_ADMIN : CONST_USER_GROUP_UNPRIVILEGED,
1801
0
          NTOP_GROUP_MAXLEN);
1802
0
      group[NTOP_GROUP_MAXLEN - 1] = '\0';
1803
0
      if (auth.allowedNets != NULL) {
1804
0
        if (!Ntop::changeAllowedNets((char*)user, auth.allowedNets)) {
1805
0
          ntop->getTrace()->traceEvent(
1806
0
              TRACE_ERROR, "HTTP: unable to set allowed nets for user %s",
1807
0
              user);
1808
0
          goto http_auth_out;
1809
0
        }
1810
0
      }
1811
0
      if (auth.allowedIfname != NULL) {
1812
0
        if (!Ntop::changeAllowedIfname((char*)user, auth.allowedIfname)) {
1813
0
          ntop->getTrace()->traceEvent(
1814
0
              TRACE_ERROR, "HTTP: unable to set allowed ifname for user %s",
1815
0
              user);
1816
0
          goto http_auth_out;
1817
0
        }
1818
0
      }
1819
0
      if (auth.language != NULL) {
1820
0
        if (!Ntop::changeUserLanguage((char*)user, auth.language)) {
1821
0
          ntop->getTrace()->traceEvent(
1822
0
              TRACE_ERROR, "HTTP: unable to set language for user %s", user);
1823
0
          goto http_auth_out;
1824
0
        }
1825
0
      }
1826
1827
0
      http_ret = true;
1828
0
    } else
1829
0
      ntop->getTrace()->traceEvent(
1830
0
          TRACE_WARNING, "HTTP: authentication rejected [code=%d]", rc);
1831
0
  } else
1832
0
    ntop->getTrace()->traceEvent(
1833
0
        TRACE_WARNING, "HTTP: could not contact the HTTP authenticator");
1834
1835
0
http_auth_out:
1836
0
  Utils::freeAuthenticator(&auth);
1837
0
  if (httpUrl) free(httpUrl);
1838
0
  if (postData) free(postData);
1839
0
  if (returnData) free(returnData);
1840
1841
0
  return (http_ret);
1842
0
}
1843
1844
/* ******************************************* */
1845
1846
bool Ntop::checkLDAPAuth(const char* user, const char* password,
1847
0
                         char* group) const {
1848
0
  bool ldap_ret = false;
1849
#if defined(NTOPNG_PRO) && defined(HAVE_LDAP)
1850
  char val[64];
1851
1852
  if (!ntop->getPro()->has_valid_license() ||
1853
      ntop->getPrefs()->limitResourcesUsage())
1854
    return false;
1855
1856
  if (ntop->getRedis()->get((char*)PREF_NTOP_LDAP_AUTH, val, sizeof(val)) < 0 ||
1857
      val[0] != '1')
1858
    return false;
1859
1860
  ntop->getTrace()->traceEvent(TRACE_INFO, "Checking LDAP auth");
1861
1862
  bool is_admin = false, has_unprivileged_capabilities = false;
1863
  char *ldapServer = NULL, *ldapAccountType = NULL, *ldapAnonymousBind = NULL,
1864
       *bind_dn = NULL, *bind_pwd = NULL, *user_group = NULL,
1865
       *search_path = NULL, *admin_group = NULL;
1866
1867
  if (!(ldapServer = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) ||
1868
      !(ldapAccountType = (char*)calloc(
1869
            sizeof(char), MAX_LDAP_LEN)) /* either 'posix' or 'samaccount' */
1870
      || !(ldapAnonymousBind = (char*)calloc(
1871
               sizeof(char), MAX_LDAP_LEN)) /* either '1' or '0' */
1872
      || !(bind_dn = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) ||
1873
      !(bind_pwd = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) ||
1874
      !(user_group = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) ||
1875
      !(search_path = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) ||
1876
      !(admin_group = (char*)calloc(sizeof(char), MAX_LDAP_LEN))) {
1877
    static bool ldap_nomem = false;
1878
1879
    if (!ldap_nomem) {
1880
      ntop->getTrace()->traceEvent(
1881
          TRACE_ERROR, "Unable to allocate memory for the LDAP authentication");
1882
      ldap_nomem = true;
1883
    }
1884
1885
    goto ldap_auth_out;
1886
  }
1887
1888
  ntop->getRedis()->get((char*)PREF_LDAP_SERVER, ldapServer, MAX_LDAP_LEN);
1889
  ntop->getRedis()->get((char*)PREF_LDAP_ACCOUNT_TYPE, ldapAccountType,
1890
                        MAX_LDAP_LEN);
1891
  ntop->getRedis()->get((char*)PREF_LDAP_BIND_ANONYMOUS, ldapAnonymousBind,
1892
                        MAX_LDAP_LEN);
1893
  ntop->getRedis()->get((char*)PREF_LDAP_BIND_DN, bind_dn, MAX_LDAP_LEN);
1894
  ntop->getRedis()->get((char*)PREF_LDAP_BIND_PWD, bind_pwd, MAX_LDAP_LEN);
1895
  ntop->getRedis()->get((char*)PREF_LDAP_SEARCH_PATH, search_path,
1896
                        MAX_LDAP_LEN);
1897
  ntop->getRedis()->get((char*)PREF_LDAP_USER_GROUP, user_group, MAX_LDAP_LEN);
1898
  ntop->getRedis()->get((char*)PREF_LDAP_ADMIN_GROUP, admin_group,
1899
                        MAX_LDAP_LEN);
1900
1901
  if (ldapServer[0]) {
1902
    ldap_ret = LdapAuthenticator::validUserLogin(
1903
        ldapServer, ldapAccountType,
1904
        (atoi(ldapAnonymousBind) == 0) ? false : true,
1905
        bind_dn[0] != '\0' ? bind_dn : NULL,
1906
        bind_pwd[0] != '\0' ? bind_pwd : NULL,
1907
        search_path[0] != '\0' ? search_path : NULL, user,
1908
        password[0] != '\0' ? password : NULL,
1909
        user_group[0] != '\0' ? user_group : NULL,
1910
        admin_group[0] != '\0' ? admin_group : NULL,
1911
        &has_unprivileged_capabilities, &is_admin);
1912
1913
    if (ldap_ret) {
1914
      if (!is_admin) {
1915
        /* Set permissions */
1916
        if (has_unprivileged_capabilities) {
1917
          changeUserPcapDownloadPermission(user, true, 86400 /* 1 day */);
1918
          changeUserHistoricalFlowPermission(user, true, 86400 /* 1 day */);
1919
          changeUserAlertsPermission(user, true, 86400 /* 1 day */);
1920
        } else {
1921
          resetUserPermissions(user);
1922
        }
1923
      }
1924
1925
      strncpy(group,
1926
              is_admin ? CONST_USER_GROUP_ADMIN : CONST_USER_GROUP_UNPRIVILEGED,
1927
              NTOP_GROUP_MAXLEN);
1928
      group[NTOP_GROUP_MAXLEN - 1] = '\0';
1929
    }
1930
  }
1931
1932
ldap_auth_out:
1933
  if (ldapServer) free(ldapServer);
1934
  if (ldapAnonymousBind) free(ldapAnonymousBind);
1935
  if (bind_dn) free(bind_dn);
1936
  if (bind_pwd) free(bind_pwd);
1937
  if (user_group) free(user_group);
1938
  if (search_path) free(search_path);
1939
  if (admin_group) free(admin_group);
1940
#endif
1941
1942
0
  return ldap_ret;
1943
0
}
1944
1945
/* ******************************************* */
1946
1947
bool Ntop::checkRadiusAuth(const char* user, const char* password,
1948
0
                           char* group) const {
1949
0
  bool radius_ret = false;
1950
#ifdef HAVE_RADIUS
1951
  bool is_admin = false, has_unprivileged_capabilities = false;
1952
  bool external_auth_for_local_users = false;
1953
  char key[64], val[64];
1954
1955
  /*
1956
     NOTE
1957
1958
     Use https://idblender.com/tools/public-radius for testing
1959
     the implementation with a public server
1960
  */
1961
1962
  if (ntop->getPrefs()->limitResourcesUsage()) return false;
1963
1964
  if (ntop->getRedis()->get((char*)PREF_NTOP_RADIUS_AUTH, val, sizeof(val)) <
1965
          0 ||
1966
      val[0] != '1')
1967
    return false;
1968
1969
  ntop->getTrace()->traceEvent(TRACE_INFO, "Checking RADIUS auth");
1970
1971
  if (!password || !password[0]) return false;
1972
1973
  if (!radiusAcc) return false;
1974
1975
  if (radiusAcc->authenticate(user, password, &has_unprivileged_capabilities,
1976
                              &is_admin)) {
1977
    if (ntop->getRedis()->get((char*)PREF_RADIUS_EXT_AUTHE_LOCAL_AUTHO, val,
1978
                              sizeof(val)) >= 0 &&
1979
        val[0] == '1')
1980
      external_auth_for_local_users = true;
1981
1982
    if (external_auth_for_local_users) {
1983
      snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, user);
1984
      if (ntop->getRedis()->get(key, val, sizeof(val)) < 0)
1985
        return false; /* Local user with same name does not exist */
1986
1987
      getUserGroupLocal(user, group);
1988
1989
    } else {
1990
      if (!is_admin) {
1991
        /* Set permissions */
1992
        if (has_unprivileged_capabilities) {
1993
          changeUserPcapDownloadPermission(user, true, 86400 /* 1 day */);
1994
          changeUserHistoricalFlowPermission(user, true, 86400 /* 1 day */);
1995
          changeUserAlertsPermission(user, true, 86400 /* 1 day */);
1996
        } else {
1997
          resetUserPermissions(user);
1998
        }
1999
      }
2000
2001
      strncpy(group,
2002
              is_admin ? CONST_USER_GROUP_ADMIN : CONST_USER_GROUP_UNPRIVILEGED,
2003
              NTOP_GROUP_MAXLEN);
2004
      group[NTOP_GROUP_MAXLEN - 1] = '\0';
2005
    }
2006
2007
    radius_ret = true;
2008
  }
2009
#endif
2010
2011
0
  return radius_ret;
2012
0
}
2013
2014
/* ******************************************* */
2015
2016
// Return 1 if username/password is allowed, 0 otherwise.
2017
bool Ntop::checkUserPassword(const char* user, const char* password,
2018
                             char* group, bool* localuser,
2019
0
                             bool* redirect_to_change_pwd) const {
2020
0
  *localuser = false;
2021
2022
0
  if (!user || user[0] == '\0' || !password || password[0] == '\0')
2023
0
    return (false);
2024
2025
  /* First of all let's check the local user authentication */
2026
0
  if (checkHTTPAuth(user, password, group)) {
2027
0
    return (true);
2028
0
  }
2029
2030
  /* Check local auth */
2031
0
  if (checkLocalAuth(user, password, group)) {
2032
0
    if ((strcmp(user, "admin") == 0) && (strcmp(password, "admin") == 0)) {
2033
      /* Force user to change password */
2034
0
      ntop->getRedis()->set((char*)CONST_DEFAULT_PASSWORD_CHANGED, (char*)"0");
2035
0
      *redirect_to_change_pwd = true;
2036
0
    }
2037
2038
    /* mark the user as local */
2039
0
    *localuser = true;
2040
0
    return (true);
2041
0
  }
2042
2043
  /* Now let's check the user by using LDAP (if available) */
2044
0
  if (checkLDAPAuth(user, password, group)) {
2045
0
    return (true);
2046
0
  }
2047
2048
  /* If the user is not a local user and not LDAP user,
2049
    let's check Radius (if available) */
2050
0
  if (checkRadiusAuth(user, password, group)) {
2051
0
    return (true);
2052
0
  }
2053
2054
0
  return (false);
2055
0
}
2056
2057
/* ******************************************* */
2058
2059
0
static int getLoginAttempts(struct mg_connection* conn) {
2060
0
  char ipbuf[32], key[128], val[16];
2061
0
  int cur_attempts = 0;
2062
0
  IpAddress client_addr;
2063
2064
0
  client_addr.set(mg_get_client_address(conn));
2065
0
  snprintf(key, sizeof(key), CONST_STR_FAILED_LOGIN_KEY,
2066
0
           client_addr.print(ipbuf, sizeof(ipbuf)));
2067
2068
0
  if ((ntop->getRedis()->get(key, val, sizeof(val)) >= 0) && val[0])
2069
0
    cur_attempts = atoi(val);
2070
2071
0
  return (cur_attempts);
2072
0
}
2073
2074
/* ******************************************* */
2075
2076
0
bool Ntop::isBlacklistedLogin(struct mg_connection* conn) const {
2077
0
  return (getLoginAttempts(conn) >= MAX_FAILED_LOGIN_ATTEMPTS);
2078
0
}
2079
2080
/* ******************************************* */
2081
2082
bool Ntop::checkGuiUserPassword(struct mg_connection* conn, const char* user,
2083
                                const char* password, char* group,
2084
                                bool* localuser,
2085
0
                                bool* redirect_to_change_pwd) const {
2086
0
  char *remote_ip, ipbuf[64], key[128], val[16];
2087
0
  int cur_attempts = 0;
2088
0
  bool rv;
2089
0
  IpAddress client_addr;
2090
2091
0
  *redirect_to_change_pwd = false;
2092
2093
0
  client_addr.set(mg_get_client_address(conn));
2094
2095
0
  if (ntop->isCaptivePortalUser(user)) {
2096
0
    ntop->getTrace()->traceEvent(
2097
0
        TRACE_WARNING, "User %s is not a gui user. Login is denied.", user);
2098
0
    rv = false;
2099
0
    goto failure;
2100
0
  }
2101
2102
0
  remote_ip = client_addr.print(ipbuf, sizeof(ipbuf));
2103
2104
0
  if ((cur_attempts = getLoginAttempts(conn)) >= MAX_FAILED_LOGIN_ATTEMPTS) {
2105
0
    ntop->getTrace()->traceEvent(TRACE_INFO,
2106
0
                                 "Login denied for '%s' from blacklisted IP %s",
2107
0
                                 user, remote_ip);
2108
0
    return false;
2109
0
  }
2110
2111
0
  rv = checkUserPassword(user, password, group, localuser,
2112
0
                         redirect_to_change_pwd);
2113
0
  snprintf(key, sizeof(key), CONST_STR_FAILED_LOGIN_KEY, remote_ip);
2114
2115
0
  if (!rv) {
2116
0
    cur_attempts++;
2117
0
    snprintf(val, sizeof(val), "%d", cur_attempts);
2118
0
    ntop->getRedis()->set(key, val, FAILED_LOGIN_ATTEMPTS_INTERVAL);
2119
2120
0
    if (cur_attempts >= MAX_FAILED_LOGIN_ATTEMPTS)
2121
0
      ntop->getTrace()->traceEvent(
2122
0
          TRACE_INFO, "IP %s is now blacklisted from login for %d seconds",
2123
0
          remote_ip, FAILED_LOGIN_ATTEMPTS_INTERVAL);
2124
2125
0
  } else {
2126
0
    ntop->getRedis()->del(key);
2127
0
  }
2128
2129
0
failure:
2130
0
  if (!rv) HTTPserver::traceLogin(user, NULL, false);
2131
2132
0
  return (rv);
2133
0
}
2134
2135
/* ******************************************* */
2136
2137
bool Ntop::checkCaptiveUserPassword(const char* user, const char* password,
2138
0
                                    char* group) const {
2139
0
  bool localuser = false;
2140
0
  bool rv, redirect_to_change_pwd = false;
2141
2142
0
  if (!ntop->isCaptivePortalUser(user)) {
2143
0
    ntop->getTrace()->traceEvent(
2144
0
        TRACE_WARNING, "User %s is not a captive portal user. Login is denied.",
2145
0
        user);
2146
0
    return false;
2147
0
  }
2148
2149
0
  rv = checkUserPassword(user, password, group, &localuser,
2150
0
                         &redirect_to_change_pwd);
2151
2152
0
  return (rv);
2153
0
}
2154
2155
/* ******************************************* */
2156
2157
0
bool Ntop::mustChangePassword(const char* user) {
2158
0
  char val[8];
2159
2160
0
  if ((strcmp(user, "admin") == 0) &&
2161
0
      ((ntop->getRedis()->get((char*)CONST_DEFAULT_PASSWORD_CHANGED, val,
2162
0
                              sizeof(val)) < 0) ||
2163
0
       val[0] == '0'))
2164
0
    return true;
2165
2166
0
  return false;
2167
0
}
2168
2169
/* ******************************************* */
2170
2171
/* NOTE: the admin vs local user checks must be performed by the caller */
2172
bool Ntop::resetUserPassword(char* username, char* old_password,
2173
0
                             char* new_password) {
2174
0
  char key[64];
2175
0
  char password_hash[33];
2176
0
  char group[NTOP_GROUP_MAXLEN];
2177
2178
0
  if ((old_password != NULL) && (old_password[0] != '\0')) {
2179
0
    bool localuser = false, redirect_to_change_pwd = false;
2180
2181
0
    if (!checkUserPassword(username, old_password, group, &localuser,
2182
0
                           &redirect_to_change_pwd))
2183
0
      return (false);
2184
2185
0
    if (!localuser) return (false);
2186
0
  }
2187
2188
0
  snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username);
2189
0
  mg_md5(password_hash, new_password, NULL);
2190
2191
0
  if (ntop->getRedis()->set(key, password_hash, 0) < 0) return (false);
2192
2193
0
  return (true);
2194
0
}
2195
2196
/* ******************************************* */
2197
2198
bool Ntop::changeUserFullName(const char* username,
2199
0
                              const char* full_name) const {
2200
0
  char key[64];
2201
2202
0
  if (username == NULL || username[0] == '\0' || full_name == NULL ||
2203
0
      !existsUser(username))
2204
0
    return false;
2205
2206
0
  ntop->getTrace()->traceEvent(TRACE_DEBUG, "Changing full name to %s for %s",
2207
0
                               full_name, username);
2208
2209
0
  snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username);
2210
0
  ntop->getRedis()->set(key, full_name, 0);
2211
2212
0
  return (ntop->getRedis()->set(key, (char*)full_name, 0) >= 0);
2213
0
}
2214
2215
/* ******************************************* */
2216
2217
0
bool Ntop::changeUserRole(char* username, char* usertype) const {
2218
0
  if (usertype != NULL) {
2219
0
    char key[64];
2220
2221
0
    snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
2222
2223
0
    if (ntop->getRedis()->set(key, usertype, 0) < 0) return (false);
2224
0
  }
2225
2226
0
  return (true);
2227
0
}
2228
2229
/* ******************************************* */
2230
2231
0
bool Ntop::changeAllowedNets(char* username, char* allowed_nets) const {
2232
0
  if (allowed_nets != NULL) {
2233
0
    char key[64];
2234
2235
0
    snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
2236
2237
0
    if (ntop->getRedis()->set(key, allowed_nets, 0) < 0) return (false);
2238
0
  }
2239
2240
0
  return (true);
2241
0
}
2242
2243
/* ******************************************* */
2244
2245
0
bool Ntop::changeAllowedIfname(char* username, char* allowed_ifname) const {
2246
  /* Add as exception :// */
2247
0
  char* column_slash = strstr(allowed_ifname, ":__");
2248
2249
0
  if (username == NULL || username[0] == '\0') return false;
2250
2251
0
  if (column_slash) column_slash[1] = column_slash[2] = '/';
2252
2253
0
  ntop->getTrace()->traceEvent(TRACE_DEBUG,
2254
0
                               "Changing allowed ifname to %s for %s",
2255
0
                               allowed_ifname, username);
2256
2257
0
  char key[64];
2258
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username);
2259
2260
0
  if (allowed_ifname != NULL && allowed_ifname[0] != '\0') {
2261
0
    return (ntop->getRedis()->set(key, allowed_ifname, 0) >= 0);
2262
0
  } else {
2263
0
    ntop->getRedis()->del(key);
2264
0
  }
2265
2266
0
  return (true);
2267
0
}
2268
2269
/* ******************************************* */
2270
2271
/*
2272
 * Set the host pool for a captive portal user on authentication
2273
 * (this is a 1-1 user-pool binding used for policy enforcement)
2274
 */
2275
bool Ntop::changeUserHostPool(const char* username,
2276
0
                              const char* host_pool_id) const {
2277
0
  if (username == NULL || username[0] == '\0') return false;
2278
2279
0
  ntop->getTrace()->traceEvent(TRACE_DEBUG,
2280
0
                               "Changing host pool id to %s for %s",
2281
0
                               host_pool_id, username);
2282
2283
0
  char key[64];
2284
0
  snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username);
2285
2286
0
  if (host_pool_id != NULL && host_pool_id[0] != '\0') {
2287
0
    return (ntop->getRedis()->set(key, (char*)host_pool_id, 0) >= 0);
2288
0
  } else {
2289
0
    ntop->getRedis()->del(key);
2290
0
  }
2291
2292
0
  return (true);
2293
0
}
2294
2295
/* ******************************************* */
2296
2297
/* Set which pools an unprivileged user is allowed to view */
2298
bool Ntop::changeAllowedHostPools(const char* username,
2299
0
                                  const char* allowed_host_pools) const {
2300
0
  if (username == NULL || username[0] == '\0') return false;
2301
2302
0
  char key[CONST_MAX_LEN_REDIS_KEY];
2303
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_HOST_POOLS, username);
2304
2305
0
  if (allowed_host_pools != NULL && allowed_host_pools[0] != '\0')
2306
0
    return (ntop->getRedis()->set(key, (char*)allowed_host_pools, 0) >= 0);
2307
0
  else
2308
0
    ntop->getRedis()->del(key);
2309
2310
0
  return (true);
2311
0
}
2312
2313
/* ******************************************* */
2314
2315
0
void Ntop::getAllowedHostPools(lua_State* vm) {
2316
0
  Bitmap4096* pools = getLuaVMUservalue(vm, allowed_pools);
2317
2318
0
  lua_newtable(vm);
2319
2320
0
  if (!pools) return;
2321
2322
0
  int bit = pools->getNext(0);
2323
0
  while (bit >= 0) {
2324
0
    lua_pushinteger(vm, bit);
2325
0
    lua_pushboolean(vm, true);
2326
0
    lua_rawset(vm, -3);
2327
0
    bit = pools->getNext(bit + 1);
2328
0
  }
2329
0
}
2330
2331
/* ******************************************* */
2332
2333
bool Ntop::changeUserLanguage(const char* username,
2334
0
                              const char* language) const {
2335
0
  if (username == NULL || username[0] == '\0') return false;
2336
2337
0
  ntop->getTrace()->traceEvent(TRACE_DEBUG, "Changing user language %s for %s",
2338
0
                               language, username);
2339
2340
0
  char key[64];
2341
0
  snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, username);
2342
2343
0
  if (language != NULL && language[0] != '\0')
2344
0
    return (ntop->getRedis()->set(key, (char*)language, 0) >= 0);
2345
0
  else
2346
0
    ntop->getRedis()->del(key);
2347
2348
0
  return (true);
2349
0
}
2350
2351
/* ******************************************* */
2352
2353
bool Ntop::changeUserPcapDownloadPermission(const char* username,
2354
                                            bool allow_pcap_download,
2355
0
                                            u_int32_t ttl) const {
2356
0
  char key[64];
2357
2358
0
  if (username == NULL || username[0] == '\0') return false;
2359
2360
0
  ntop->getTrace()->traceEvent(
2361
0
      TRACE_DEBUG, "Changing user permission [allow-pcap-download: %s] for %s",
2362
0
      allow_pcap_download ? "true" : "false", username);
2363
2364
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
2365
2366
0
  if (allow_pcap_download)
2367
0
    return (ntop->getRedis()->set(key, "1", ttl) >= 0);
2368
0
  else
2369
0
    ntop->getRedis()->del(key);
2370
2371
0
  return (true);
2372
0
}
2373
2374
/* ******************************************* */
2375
2376
bool Ntop::changeUserHistoricalFlowPermission(const char* username,
2377
                                              bool allow_historical_flows,
2378
0
                                              u_int32_t ttl) const {
2379
0
  char key[64];
2380
2381
0
  if (username == NULL || username[0] == '\0') return false;
2382
2383
0
  ntop->getTrace()->traceEvent(
2384
0
      TRACE_DEBUG,
2385
0
      "Changing user permission [allow-historical-flow: %s] for %s",
2386
0
      allow_historical_flows ? "true" : "false", username);
2387
2388
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
2389
2390
0
  if (allow_historical_flows)
2391
0
    return (ntop->getRedis()->set(key, "1", ttl) >= 0);
2392
0
  else
2393
0
    ntop->getRedis()->del(key);
2394
2395
0
  return (true);
2396
0
}
2397
2398
/* ******************************************* */
2399
2400
bool Ntop::changeUserAlertsPermission(const char* username, bool allow_alerts,
2401
0
                                      u_int32_t ttl) const {
2402
0
  char key[64];
2403
2404
0
  if (username == NULL || username[0] == '\0') return false;
2405
2406
0
  ntop->getTrace()->traceEvent(
2407
0
      TRACE_DEBUG, "Changing user permission [allow-alerts: %s] for %s",
2408
0
      allow_alerts ? "true" : "false", username);
2409
2410
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_ALERTS, username);
2411
2412
0
  if (allow_alerts)
2413
0
    return (ntop->getRedis()->set(key, "1", ttl) >= 0);
2414
0
  else
2415
0
    ntop->getRedis()->del(key);
2416
2417
0
  return (true);
2418
0
}
2419
2420
/* ******************************************* */
2421
2422
0
void Ntop::resetUserPermissions(const char* user) const {
2423
0
  char key[64];
2424
2425
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, user);
2426
0
  ntop->getRedis()->del(key);
2427
2428
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, user);
2429
0
  ntop->getRedis()->del(key);
2430
2431
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_ALERTS, user);
2432
0
  ntop->getRedis()->del(key);
2433
0
}
2434
2435
/* ******************************************* */
2436
2437
0
bool Ntop::hasCapability(lua_State* vm, UserCapabilities capability) {
2438
0
  u_int64_t capabilities = getLuaVMUservalue(vm, capabilities);
2439
0
  return !!(capabilities & (1 << capability));
2440
0
}
2441
2442
/* ******************************************* */
2443
2444
bool Ntop::getUserCapabilities(const char* username, bool* allow_pcap_download,
2445
                               bool* allow_historical_flows,
2446
0
                               bool* allow_alerts) const {
2447
0
  char key[64], val[2];
2448
2449
0
  *allow_pcap_download = *allow_historical_flows = *allow_alerts = false;
2450
2451
0
  if (username == NULL || username[0] == '\0') return (false);
2452
2453
  /* ************** */
2454
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
2455
2456
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
2457
0
    if (strcmp(val, "1") == 0) *allow_pcap_download = true;
2458
2459
  /* ************** */
2460
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
2461
2462
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
2463
0
    if (strcmp(val, "1") == 0) *allow_historical_flows = true;
2464
2465
  /* ************** */
2466
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_ALERTS, username);
2467
2468
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
2469
0
    if (strcmp(val, "1") == 0) *allow_alerts = true;
2470
2471
0
  return (true);
2472
0
}
2473
2474
/* ******************************************* */
2475
2476
/*
2477
  Assigns a unique user id to be assigned to a new ntopng user. Callers must
2478
  lock. Assigned user id, if assignment is successful, is returned in the first
2479
  param.
2480
 */
2481
0
bool Ntop::assignUserId(u_int8_t* new_user_id) {
2482
0
  char cur_id_buf[8];
2483
0
  int cur_id;
2484
2485
0
  for (cur_id = 0; cur_id < NTOP_MAX_NUM_USERS; cur_id++) {
2486
0
    snprintf(cur_id_buf, sizeof(cur_id_buf), "%d", cur_id);
2487
2488
0
    if (!ntop->getRedis()->sismember(PREF_NTOP_USER_IDS, cur_id_buf)) break;
2489
0
  }
2490
2491
0
  if (cur_id == NTOP_MAX_NUM_USERS) return false; /* No more ids available */
2492
2493
0
  if (ntop->getRedis()->sadd(PREF_NTOP_USER_IDS, cur_id_buf) < 0)
2494
0
    return false; /* Unable to add the newly assigned user id to the set of all
2495
                     user ids */
2496
2497
0
  *new_user_id = cur_id;
2498
2499
0
  ntop->getTrace()->traceEvent(TRACE_DEBUG, "Assigned user id %u",
2500
0
                               *new_user_id);
2501
2502
0
  return true;
2503
0
}
2504
2505
/* ******************************************* */
2506
2507
0
bool Ntop::existsUser(const char* username) const {
2508
0
  char key[CONST_MAX_LEN_REDIS_KEY], val[2] /* Don't care about the content */;
2509
2510
0
  snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
2511
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
2512
0
    return (true);  // user already exists
2513
2514
0
  return (false);
2515
0
}
2516
2517
/* ******************************************* */
2518
2519
bool Ntop::addUser(char* username, char* full_name, char* password,
2520
                   char* host_role, char* allowed_networks,
2521
                   char* allowed_ifname, char* host_pool_id, char* language,
2522
                   bool allow_pcap_download, bool allow_historical_flows,
2523
0
                   bool allow_alerts, char* allowed_host_pools) {
2524
0
  char key[CONST_MAX_LEN_REDIS_KEY];
2525
0
  char password_hash[33];
2526
0
  char new_user_id_buf[8];
2527
0
  u_int8_t new_user_id = 0;
2528
2529
0
  users_m.lock(__FILE__, __LINE__);
2530
2531
0
  if (existsUser(username) /* User already existing */
2532
0
      || !assignUserId(&new_user_id) /* Unable to assign a user id */) {
2533
0
    users_m.unlock(__FILE__, __LINE__);
2534
0
    return (false);
2535
0
  }
2536
2537
0
  snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username);
2538
0
  ntop->getRedis()->set(key, full_name, 0);
2539
2540
0
  snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
2541
0
  ntop->getRedis()->set(key, (char*)host_role, 0);
2542
2543
0
  snprintf(key, sizeof(key), CONST_STR_USER_ID, username);
2544
0
  snprintf(new_user_id_buf, sizeof(new_user_id_buf), "%d", new_user_id);
2545
0
  ntop->getRedis()->set(key, new_user_id_buf, 0);
2546
2547
0
  snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username);
2548
0
  mg_md5(password_hash, password, NULL);
2549
0
  ntop->getRedis()->set(key, password_hash, 0);
2550
2551
0
  snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
2552
0
  ntop->getRedis()->set(key, allowed_networks, 0);
2553
2554
0
  snprintf(key, sizeof(key), CONST_STR_USER_THEME, username);
2555
0
  ntop->getRedis()->set(key, "", 0);
2556
2557
0
  snprintf(key, sizeof(key), CONST_STR_USER_DATE_FORMAT, username);
2558
0
  ntop->getRedis()->set(key, "", 0);
2559
2560
0
  if (language && language[0] != '\0') {
2561
0
    snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, username);
2562
0
    ntop->getRedis()->set(key, language, 0);
2563
0
  }
2564
2565
0
  if (allow_pcap_download) {
2566
0
    snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
2567
0
    ntop->getRedis()->set(key, "1", 0);
2568
0
  }
2569
2570
0
  if (allow_historical_flows) {
2571
0
    snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
2572
0
    ntop->getRedis()->set(key, "1", 0);
2573
0
  }
2574
2575
0
  if (allow_alerts) {
2576
0
    snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_ALERTS, username);
2577
0
    ntop->getRedis()->set(key, "1", 0);
2578
0
  }
2579
2580
0
  if (allowed_ifname && allowed_ifname[0] != '\0') {
2581
0
    ntop->getTrace()->traceEvent(TRACE_DEBUG, "Setting allowed ifname: %s",
2582
0
                                 allowed_ifname);
2583
0
    snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username);
2584
0
    ntop->getRedis()->set(key, allowed_ifname, 0);
2585
0
  }
2586
2587
0
  if (host_pool_id && host_pool_id[0] != '\0') {
2588
0
    snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username);
2589
0
    ntop->getRedis()->set(key, host_pool_id, 0);
2590
0
  }
2591
2592
0
  if (allowed_host_pools && allowed_host_pools[0] != '\0') {
2593
0
    snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_HOST_POOLS, username);
2594
0
    ntop->getRedis()->set(key, allowed_host_pools, 0);
2595
0
  }
2596
2597
0
  users_m.unlock(__FILE__, __LINE__);
2598
2599
0
  return (true);
2600
0
}
2601
2602
/* ******************************************* */
2603
2604
0
bool Ntop::addUserAPIToken(const char* username, const char* api_token) {
2605
0
  char key[CONST_MAX_LEN_REDIS_KEY];
2606
2607
0
  snprintf(key, sizeof(key), CONST_STR_USER_API_TOKEN, username);
2608
0
  ntop->getRedis()->set(key, api_token);
2609
2610
0
  return (true);
2611
0
}
2612
2613
/* ******************************************* */
2614
2615
/* TOTP/MFA implementation based on RFC 6238 (TOTP) and RFC 4226 (HOTP).
2616
 * Uses OpenSSL HMAC-SHA1 which is already a dependency of ntopng.
2617
 * Secrets are Base32-encoded (RFC 4648) as required by the otpauth:// URI
2618
 * scheme.
2619
 */
2620
2621
static const char* base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
2622
2623
/* Encode binary data to Base32 string. Returns the number of bytes written. */
2624
static int base32_encode(const uint8_t* src, int src_len, char* dst,
2625
0
                         int dst_len) {
2626
0
  int i, written = 0;
2627
0
  int bits = 0, val = 0;
2628
2629
0
  for (i = 0; i < src_len && written < dst_len - 1; i++) {
2630
0
    val = (val << 8) | src[i];
2631
0
    bits += 8;
2632
0
    while (bits >= 5 && written < dst_len - 1) {
2633
0
      dst[written++] = base32_alphabet[(val >> (bits - 5)) & 0x1F];
2634
0
      bits -= 5;
2635
0
    }
2636
0
  }
2637
0
  if (bits > 0 && written < dst_len - 1)
2638
0
    dst[written++] = base32_alphabet[(val << (5 - bits)) & 0x1F];
2639
0
  dst[written] = '\0';
2640
0
  return written;
2641
0
}
2642
2643
/* Decode Base32 string to binary. Returns number of bytes decoded, or -1 on
2644
 * error. */
2645
0
static int base32_decode(const char* src, uint8_t* dst, int dst_len) {
2646
0
  int val = 0, bits = 0, written = 0;
2647
0
  for (int i = 0; src[i] != '\0'; i++) {
2648
0
    char c = toupper((unsigned char)src[i]);
2649
0
    const char* p = strchr(base32_alphabet, c);
2650
0
    if (!p) {
2651
0
      if (c == '=') break; /* padding */
2652
0
      return -1;           /* invalid character */
2653
0
    }
2654
0
    val = (val << 5) | (int)(p - base32_alphabet);
2655
0
    bits += 5;
2656
0
    if (bits >= 8) {
2657
0
      if (written >= dst_len) return -1;
2658
0
      dst[written++] = (uint8_t)((val >> (bits - 8)) & 0xFF);
2659
0
      bits -= 8;
2660
0
    }
2661
0
  }
2662
0
  return written;
2663
0
}
2664
2665
/* ********************************************************* */
2666
2667
/* Compute TOTP code for the given secret (Base32) and time counter.
2668
 * Returns a 6-digit code or -1 on error.
2669
 */
2670
2671
#include <openssl/opensslv.h>
2672
2673
/* --- Conditional Header Selection Based on OpenSSL Version --- */
2674
#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
2675
#include <openssl/evp.h>
2676
#include <openssl/params.h>
2677
#include <openssl/core_names.h>   /* OSSL_MAC_PARAM_DIGEST lives here */
2678
/* Fallback in case a stale/mismatched core_names.h is picked up on the
2679
 * include path (common on macOS with multiple OpenSSL installs). The
2680
 * value is a stable part of the OpenSSL 3.0 API/ABI, so hardcoding it
2681
 * here is safe if the header lookup fails. */
2682
#else
2683
#include <openssl/hmac.h>
2684
#include <openssl/evp.h>
2685
#endif
2686
2687
0
static int compute_totp(const char* secret_b32, uint64_t counter) {
2688
0
  uint8_t secret[64];
2689
0
  int secret_len = base32_decode(secret_b32, secret, sizeof(secret));
2690
0
  if (secret_len <= 0) return -1;
2691
2692
  /* Build 8-byte big-endian counter */
2693
0
  uint8_t msg[8];
2694
0
  for (int i = 7; i >= 0; i--) {
2695
0
    msg[i] = counter & 0xFF;
2696
0
    counter >>= 8;
2697
0
  }
2698
2699
  /* --- OPENSSL COMPATIBLE HMAC-SHA1 --- */
2700
0
  unsigned char hmac[20];
2701
0
  int success = 0;
2702
2703
#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
2704
  /* --- OPENSSL 3.x EVP_MAC Architecture --- */
2705
  size_t hmac_len = sizeof(hmac);
2706
  EVP_MAC* mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
2707
  if (mac != NULL) {
2708
    EVP_MAC_CTX* ctx = EVP_MAC_CTX_new(mac);
2709
    if (ctx != NULL) {
2710
      OSSL_PARAM params[2];
2711
2712
      // Corrected parameters configuration
2713
      params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, (char *)"SHA1", 0);
2714
      params[1] = OSSL_PARAM_construct_end();
2715
2716
      if (EVP_MAC_init(ctx, secret, secret_len, params) == 1 &&
2717
    EVP_MAC_update(ctx, msg, sizeof(msg)) == 1 &&
2718
    EVP_MAC_final(ctx, hmac, &hmac_len, sizeof(hmac)) == 1) {
2719
  success = 1;
2720
      }
2721
      EVP_MAC_CTX_free(ctx);
2722
    }
2723
    EVP_MAC_free(mac);
2724
  }
2725
  #else
2726
  /* --- OPENSSL 1.1.x HMAC_CTX Architecture --- */
2727
0
  unsigned int hmac_len = sizeof(hmac);
2728
0
  HMAC_CTX* ctx = HMAC_CTX_new();
2729
0
  if (ctx != NULL) {
2730
0
    if (HMAC_Init_ex(ctx, secret, secret_len, EVP_sha1(), NULL) == 1 &&
2731
0
  HMAC_Update(ctx, msg, sizeof(msg)) == 1 &&
2732
0
  HMAC_Final(ctx, hmac, &hmac_len) == 1) {
2733
0
      success = 1;
2734
0
    }
2735
0
    HMAC_CTX_free(ctx);
2736
0
  }
2737
0
  #endif
2738
  /* ------------------------------------------- */
2739
2740
0
  if (!success) return -1;
2741
2742
  /* Dynamic truncation (RFC 4226 B'5.3) */
2743
0
  int offset = hmac[19] & 0x0F;
2744
0
    uint32_t code =
2745
0
      ((hmac[offset] & 0x7F) << 24) | ((hmac[offset + 1] & 0xFF) << 16) |
2746
0
      ((hmac[offset + 2] & 0xFF) << 8) | ((hmac[offset + 3] & 0xFF));
2747
2748
0
    return (int)(code % 1000000);
2749
0
}
2750
2751
/* ******************************************* */
2752
2753
0
bool Ntop::generateTOTPSecret(char* secret, size_t secret_len) const {
2754
  /* Generate 20 random bytes and Base32-encode them */
2755
0
  uint8_t raw[20];
2756
0
  if (RAND_bytes(raw, sizeof(raw)) != 1) return false;
2757
0
  base32_encode(raw, sizeof(raw), secret, (int)secret_len);
2758
0
  return true;
2759
0
}
2760
2761
/* ******************************************* */
2762
2763
0
bool Ntop::setUserTOTPSecret(const char* username, const char* secret) const {
2764
0
  char key[CONST_MAX_LEN_REDIS_KEY];
2765
0
  snprintf(key, sizeof(key), CONST_STR_USER_TOTP_SECRET, username);
2766
0
  return (ntop->getRedis()->set(key, (char*)secret, 0) >= 0);
2767
0
}
2768
2769
/* ******************************************* */
2770
2771
bool Ntop::getUserTOTPSecret(const char* username, char* secret,
2772
0
                             size_t secret_len) const {
2773
0
  char key[CONST_MAX_LEN_REDIS_KEY];
2774
0
  snprintf(key, sizeof(key), CONST_STR_USER_TOTP_SECRET, username);
2775
0
  return (ntop->getRedis()->get(key, secret, (u_int)secret_len) >= 0);
2776
0
}
2777
2778
/* ******************************************* */
2779
2780
0
bool Ntop::isTOTPEnabled(const char* username) const {
2781
0
  char key[CONST_MAX_LEN_REDIS_KEY], val[4];
2782
0
  snprintf(key, sizeof(key), CONST_STR_USER_TOTP_ENABLED, username);
2783
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) < 0) return false;
2784
0
  return (val[0] == '1');
2785
0
}
2786
2787
/* ******************************************* */
2788
2789
0
bool Ntop::setUserTOTPEnabled(const char* username, bool enabled) const {
2790
0
  char key[CONST_MAX_LEN_REDIS_KEY];
2791
0
  snprintf(key, sizeof(key), CONST_STR_USER_TOTP_ENABLED, username);
2792
0
  return (ntop->getRedis()->set(key, enabled ? (char*)"1" : (char*)"0", 0) >=
2793
0
          0);
2794
0
}
2795
2796
/* ******************************************* */
2797
2798
0
bool Ntop::validateTOTPCode(const char* username, const char* code) const {
2799
0
  char secret[64];
2800
0
  if (!getUserTOTPSecret(username, secret, sizeof(secret))) return false;
2801
0
  if (strlen(code) != 6) return false;
2802
0
  for (int i = 0; code[i]; i++)
2803
0
    if (!isdigit((unsigned char)code[i])) return false;
2804
2805
0
  int input_code = atoi(code);
2806
0
  uint64_t counter = (uint64_t)(time(NULL) / 30);
2807
2808
  /* Allow ±1 window (90 seconds total) to handle clock skew */
2809
0
  for (int delta = -1; delta <= 1; delta++) {
2810
0
    if (compute_totp(secret, counter + (uint64_t)delta) == input_code)
2811
0
      return true;
2812
0
  }
2813
0
  return false;
2814
0
}
2815
2816
/* ******************************************* */
2817
2818
void Ntop::getTOTPProvisioningUri(const char* username, char* uri,
2819
0
                                  size_t uri_len) const {
2820
0
  char secret[64];
2821
0
  if (!getUserTOTPSecret(username, secret, sizeof(secret))) {
2822
0
    uri[0] = '\0';
2823
0
    return;
2824
0
  }
2825
0
  const char* issuer = "ntopng";
2826
0
  snprintf(uri, uri_len,
2827
0
           "otpauth://totp/%s:%s?secret=%s&issuer=%s&digits=6&period=30",
2828
0
           issuer, username, secret, issuer);
2829
0
}
2830
2831
/* ******************************************* */
2832
2833
bool Ntop::createMFAPendingToken(const char* username, const char* referer,
2834
0
                                 char* token, size_t token_len) const {
2835
0
  char val[512], key[128];
2836
0
  char random[64], tmp_token[33];
2837
0
  srand((int)time(0) ^ (int)getpid());
2838
0
  snprintf(random, sizeof(random), "%d%s", rand(), username);
2839
  /* mg_md5 produces a 33-char (32 hex + NUL) string */
2840
0
  mg_md5(tmp_token, random, NULL);
2841
0
  strncpy(token, tmp_token, token_len - 1);
2842
0
  token[token_len - 1] = '\0';
2843
2844
0
  snprintf(key, sizeof(key), "%s.%s", MFA_PENDING_PREFIX, token);
2845
0
  snprintf(val, sizeof(val), "%s|%s", username, referer ? referer : "/");
2846
0
  return (ntop->getRedis()->set(key, val, MFA_PENDING_TTL) >= 0);
2847
0
}
2848
2849
/* ******************************************* */
2850
2851
bool Ntop::getMFAPendingToken(const char* token, char* username,
2852
                              size_t username_len, char* referer,
2853
0
                              size_t referer_len) const {
2854
0
  char key[128], val[512];
2855
0
  snprintf(key, sizeof(key), "%s.%s", MFA_PENDING_PREFIX, token);
2856
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) < 0) return false;
2857
2858
0
  char* sep = strchr(val, '|');
2859
0
  if (!sep) return false;
2860
0
  *sep = '\0';
2861
0
  strncpy(username, val, username_len - 1);
2862
0
  username[username_len - 1] = '\0';
2863
0
  strncpy(referer, sep + 1, referer_len - 1);
2864
0
  referer[referer_len - 1] = '\0';
2865
0
  return true;
2866
0
}
2867
2868
/* ******************************************* */
2869
2870
0
void Ntop::deleteMFAPendingToken(const char* token) const {
2871
0
  char key[128];
2872
0
  snprintf(key, sizeof(key), "%s.%s", MFA_PENDING_PREFIX, token);
2873
0
  ntop->getRedis()->del(key);
2874
0
}
2875
2876
/* ******************************************* */
2877
2878
/* ========================================================================
2879
 * WebAuthn / Passkey implementation
2880
 * Supports ES256 (P-256 / secp256r1) credentials.
2881
 * Requires OpenSSL (already a project dependency) for:
2882
 *   - RAND_bytes (random challenge generation)
2883
 *   - SHA256 (rpId hash + clientDataJSON hash)
2884
 *   - EC_KEY, ECDSA_verify (P-256 signature verification)
2885
 * ======================================================================== */
2886
2887
/* ---------- Base64url helpers ---------- */
2888
2889
0
static std::string webauthn_b64url_encode(const uint8_t* data, size_t len) {
2890
0
  static const char T[] =
2891
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2892
0
  std::string out;
2893
0
  out.reserve((len + 2) / 3 * 4);
2894
0
  for (size_t i = 0; i < len; i += 3) {
2895
0
    uint32_t b = (uint32_t)data[i] << 16;
2896
0
    if (i + 1 < len) b |= (uint32_t)data[i + 1] << 8;
2897
0
    if (i + 2 < len) b |= data[i + 2];
2898
0
    int n = (i + 1 < len) ? ((i + 2 < len) ? 4 : 3) : 2;
2899
0
    out += T[(b >> 18) & 0x3F];
2900
0
    out += T[(b >> 12) & 0x3F];
2901
0
    if (n >= 3) out += T[(b >> 6) & 0x3F];
2902
0
    if (n >= 4) out += T[b & 0x3F];
2903
0
  }
2904
0
  return out;
2905
0
}
2906
2907
/* Returns number of decoded bytes or -1 on error. */
2908
static int webauthn_b64url_decode(const char* src, uint8_t* dst,
2909
0
                                   int dst_max) {
2910
0
  int8_t V[256];
2911
0
  memset(V, -1, sizeof(V));
2912
0
  for (int i = 0; i < 26; i++) V[(uint8_t)('A' + i)] = (int8_t)i;
2913
0
  for (int i = 0; i < 26; i++) V[(uint8_t)('a' + i)] = (int8_t)(26 + i);
2914
0
  for (int i = 0; i < 10; i++) V[(uint8_t)('0' + i)] = (int8_t)(52 + i);
2915
0
  V[(uint8_t)'+'] = 62; V[(uint8_t)'-'] = 62;
2916
0
  V[(uint8_t)'/'] = 63; V[(uint8_t)'_'] = 63;
2917
0
  V[(uint8_t)'='] = -2; /* padding - stop */
2918
2919
0
  int out = 0;
2920
0
  uint32_t acc = 0;
2921
0
  int bits = 0;
2922
0
  for (int i = 0; src[i]; i++) {
2923
0
    unsigned char c = (unsigned char)src[i];
2924
0
    if (c >= 128) return -1;
2925
0
    int8_t v = V[c];
2926
0
    if (v == -2) break;
2927
0
    if (v < 0) return -1;
2928
0
    acc = (acc << 6) | (uint32_t)v;
2929
0
    bits += 6;
2930
0
    if (bits >= 8) {
2931
0
      if (out >= dst_max) return -1;
2932
0
      dst[out++] = (uint8_t)((acc >> (bits - 8)) & 0xFF);
2933
0
      bits -= 8;
2934
0
    }
2935
0
  }
2936
0
  return out;
2937
0
}
2938
2939
/* ---------- Minimal CBOR parser ---------- */
2940
/* Supports: uint (0), negint (1), bytes (2), text (3), array (4), map (5) */
2941
2942
struct cbor_val {
2943
  uint8_t  type;   /* major type 0-7 */
2944
  int64_t  i;      /* for uint/negint */
2945
  const uint8_t* p; /* pointer into source for bytes/text */
2946
  size_t   n;      /* byte length for bytes/text; item count for array/map */
2947
};
2948
2949
0
static uint64_t cbor_get_arg(const uint8_t* buf, size_t buf_len, size_t* off) {
2950
0
  if (*off >= buf_len) return UINT64_MAX;
2951
0
  uint8_t ai = buf[*off] & 0x1F;
2952
0
  (*off)++;
2953
0
  if (ai < 24) return ai;
2954
0
  if (ai == 24) {
2955
0
    if (*off >= buf_len) return UINT64_MAX;
2956
0
    return buf[(*off)++];
2957
0
  }
2958
0
  if (ai == 25) {
2959
0
    if (*off + 2 > buf_len) return UINT64_MAX;
2960
0
    uint64_t v = ((uint64_t)buf[*off] << 8) | buf[*off + 1];
2961
0
    *off += 2;
2962
0
    return v;
2963
0
  }
2964
0
  if (ai == 26) {
2965
0
    if (*off + 4 > buf_len) return UINT64_MAX;
2966
0
    uint64_t v = ((uint64_t)buf[*off] << 24) | ((uint64_t)buf[*off + 1] << 16) |
2967
0
                 ((uint64_t)buf[*off + 2] << 8) | buf[*off + 3];
2968
0
    *off += 4;
2969
0
    return v;
2970
0
  }
2971
0
  return UINT64_MAX; /* 8-byte or indefinite: not supported */
2972
0
}
2973
2974
static bool cbor_next(const uint8_t* buf, size_t buf_len, size_t* off,
2975
0
                       cbor_val* v) {
2976
0
  if (*off >= buf_len) return false;
2977
0
  v->type = (buf[*off] >> 5) & 7;
2978
0
  uint64_t n = cbor_get_arg(buf, buf_len, off);
2979
0
  if (n == UINT64_MAX) return false;
2980
0
  switch (v->type) {
2981
0
    case 0: v->i = (int64_t)n; return true;
2982
0
    case 1: v->i = -(int64_t)n - 1; return true;
2983
0
    case 2: case 3:
2984
0
      if (*off + n > buf_len) return false;
2985
0
      v->p = buf + *off; v->n = (size_t)n; *off += n; return true;
2986
0
    case 4: case 5:
2987
0
      v->n = (size_t)n; return true;
2988
0
    default: return false;
2989
0
  }
2990
0
}
2991
2992
0
static bool cbor_skip(const uint8_t* buf, size_t buf_len, size_t* off) {
2993
0
  cbor_val v;
2994
0
  if (!cbor_next(buf, buf_len, off, &v)) return false;
2995
0
  size_t count = (v.type == 4) ? v.n : (v.type == 5) ? v.n * 2 : 0;
2996
0
  for (size_t i = 0; i < count; i++)
2997
0
    if (!cbor_skip(buf, buf_len, off)) return false;
2998
0
  return true;
2999
0
}
3000
3001
/* ---------- COSE / authenticatorData parsing ---------- */
3002
3003
/* Parse a COSE EC2 public key (P-256 / ES256).
3004
 * Expected CBOR map: {1:2, 3:-7, -1:1, -2:<x 32B>, -3:<y 32B>}
3005
 * Returns true and sets x_out/y_out (each 32 bytes) on success. */
3006
static bool parse_cose_ec2(const uint8_t* cbor, size_t cbor_len,
3007
0
                             uint8_t* x_out, uint8_t* y_out) {
3008
0
  size_t off = 0;
3009
0
  cbor_val v;
3010
0
  if (!cbor_next(cbor, cbor_len, &off, &v) || v.type != 5) return false;
3011
0
  size_t map_items = v.n;
3012
0
  bool got_x = false, got_y = false;
3013
0
  for (size_t i = 0; i < map_items; i++) {
3014
0
    cbor_val key, val;
3015
0
    if (!cbor_next(cbor, cbor_len, &off, &key)) return false;
3016
0
    if (!cbor_next(cbor, cbor_len, &off, &val)) return false;
3017
0
    if (key.type == 0 || key.type == 1) {
3018
0
      if (key.i == -2 && val.type == 2 && val.n == 32) {
3019
0
        memcpy(x_out, val.p, 32); got_x = true;
3020
0
      } else if (key.i == -3 && val.type == 2 && val.n == 32) {
3021
0
        memcpy(y_out, val.p, 32); got_y = true;
3022
0
      }
3023
0
    }
3024
0
  }
3025
0
  return got_x && got_y;
3026
0
}
3027
3028
/* Extract the authData bytes from a CBOR-encoded attestationObject.
3029
 * Returns a pointer into the cbor buffer and sets *len on success. */
3030
static bool extract_auth_data(const uint8_t* cbor, size_t cbor_len,
3031
                               const uint8_t** auth_data_out,
3032
0
                               size_t* auth_data_len_out) {
3033
0
  size_t off = 0;
3034
0
  cbor_val v;
3035
0
  if (!cbor_next(cbor, cbor_len, &off, &v) || v.type != 5) return false;
3036
0
  size_t map_items = v.n;
3037
0
  for (size_t i = 0; i < map_items; i++) {
3038
0
    cbor_val key;
3039
0
    if (!cbor_next(cbor, cbor_len, &off, &key)) return false;
3040
0
    if (key.type == 3 && key.n == 8 &&
3041
0
        memcmp(key.p, "authData", 8) == 0) {
3042
0
      cbor_val val;
3043
0
      if (!cbor_next(cbor, cbor_len, &off, &val) || val.type != 2)
3044
0
        return false;
3045
0
      *auth_data_out = val.p;
3046
0
      *auth_data_len_out = val.n;
3047
0
      return true;
3048
0
    }
3049
0
    if (!cbor_skip(cbor, cbor_len, &off)) return false;
3050
0
  }
3051
0
  return false;
3052
0
}
3053
3054
struct wa_auth_data {
3055
  uint8_t  rp_id_hash[32];
3056
  uint8_t  flags;
3057
  uint32_t sign_count;
3058
  uint8_t  cred_id[512];
3059
  size_t   cred_id_len;
3060
  uint8_t  pk_x[32];
3061
  uint8_t  pk_y[32];
3062
  bool     has_cred;
3063
};
3064
3065
/* Parse the binary authenticatorData structure. */
3066
0
static bool parse_auth_data(const uint8_t* d, size_t len, wa_auth_data* out) {
3067
0
  if (len < 37) return false;
3068
0
  memcpy(out->rp_id_hash, d, 32);
3069
0
  out->flags = d[32];
3070
0
  out->sign_count = ((uint32_t)d[33] << 24) | ((uint32_t)d[34] << 16) |
3071
0
                    ((uint32_t)d[35] << 8) | d[36];
3072
0
  out->has_cred = (out->flags & 0x40) != 0;
3073
0
  out->cred_id_len = 0;
3074
0
  if (out->has_cred) {
3075
0
    if (len < 37 + 18) return false;          /* aaguid(16) + credIdLen(2) */
3076
0
    size_t pos = 37 + 16;                      /* skip aaguid */
3077
0
    uint16_t cid_len = ((uint16_t)d[pos] << 8) | d[pos + 1];
3078
0
    pos += 2;
3079
0
    if (len < pos + cid_len || cid_len > sizeof(out->cred_id)) return false;
3080
0
    memcpy(out->cred_id, d + pos, cid_len);
3081
0
    out->cred_id_len = cid_len;
3082
0
    pos += cid_len;
3083
0
    if (!parse_cose_ec2(d + pos, len - pos, out->pk_x, out->pk_y))
3084
0
      return false;
3085
0
  }
3086
0
  return true;
3087
0
}
3088
3089
/* ---------- Minimal JSON field extractor ---------- */
3090
3091
/* Extract a JSON string value for key from a compact JSON object.
3092
 * Works for browser-generated clientDataJSON which is compact and ASCII. */
3093
static bool json_get_str(const char* json, const char* key,
3094
0
                          char* out, size_t out_len) {
3095
0
  char needle[128];
3096
0
  snprintf(needle, sizeof(needle), "\"%s\":\"", key);
3097
0
  const char* p = strstr(json, needle);
3098
0
  if (!p) return false;
3099
0
  p += strlen(needle);
3100
0
  size_t i = 0;
3101
0
  while (*p && *p != '"' && i < out_len - 1) {
3102
0
    if (*p == '\\') { p++; if (!*p) break; }
3103
0
    out[i++] = *p++;
3104
0
  }
3105
0
  out[i] = '\0';
3106
0
  return *p == '"';
3107
0
}
3108
3109
/* ---------- ECDSA P-256 verification ---------- */
3110
3111
/* Verify a DER-encoded ECDSA-P256 signature over data using public key (x,y).
3112
 * Internally hashes data with SHA-256 before verifying. */
3113
static bool verify_ecdsa_p256(const uint8_t* pk_x, const uint8_t* pk_y,
3114
            const uint8_t* data, size_t data_len,
3115
0
            const uint8_t* sig, size_t sig_len) {
3116
#ifdef HAVE_OPENSSL_CORE_NAMES_H
3117
  /* OpenSSL 3 or later */
3118
  bool ok = false;
3119
  EVP_PKEY* pkey = NULL;
3120
  EVP_PKEY_CTX* ctx = NULL;
3121
  OSSL_PARAM_BLD* param_bld = OSSL_PARAM_BLD_new();
3122
  OSSL_PARAM* params = NULL;
3123
3124
  // ui public key parameters (P-256)
3125
  OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME, "prime256v1", 0);
3126
  OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_EC_PUB_X, BN_bin2bn(pk_x, 32, NULL));
3127
  OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_EC_PUB_Y, BN_bin2bn(pk_y, 32, NULL));
3128
3129
  params = OSSL_PARAM_BLD_to_param(param_bld);
3130
  ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
3131
3132
  if (!ctx || EVP_PKEY_fromdata_init(ctx) <= 0 ||
3133
      EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
3134
    goto cleanup;
3135
  }
3136
3137
  // Check signature
3138
  {
3139
    EVP_MD_CTX* mctx = EVP_MD_CTX_new();
3140
    
3141
    if (EVP_DigestVerifyInit(mctx, NULL, EVP_sha256(), NULL, pkey) > 0) {
3142
      ok = (EVP_DigestVerify(mctx, sig, sig_len, data, data_len) == 1);
3143
    }
3144
    
3145
    EVP_MD_CTX_free(mctx);
3146
  }
3147
3148
 cleanup:
3149
  OSSL_PARAM_BLD_free(param_bld);
3150
  OSSL_PARAM_free(params);
3151
  EVP_PKEY_free(pkey);
3152
  EVP_PKEY_CTX_free(ctx);
3153
3154
  return ok;
3155
#else
3156
0
  bool ok = false;
3157
0
  EC_KEY* key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
3158
0
  if (!key) return false;
3159
3160
0
  BIGNUM* bx = BN_bin2bn(pk_x, 32, NULL);
3161
0
  BIGNUM* by = BN_bin2bn(pk_y, 32, NULL);
3162
0
  if (!bx || !by) goto done;
3163
0
  if (!EC_KEY_set_public_key_affine_coordinates(key, bx, by)) goto done;
3164
3165
0
  {
3166
0
    uint8_t hash[32];
3167
0
    SHA256(data, data_len, hash);
3168
0
    ok = (ECDSA_verify(0, hash, (int)sizeof(hash), sig, sig_len, key) == 1);
3169
0
  }
3170
0
 done:
3171
0
  if (bx) BN_free(bx);
3172
0
  if (by) BN_free(by);
3173
0
  EC_KEY_free(key);
3174
3175
0
  return ok;
3176
0
#endif
3177
0
}
3178
            
3179
3180
/* ---------- Ntop WebAuthn method implementations ---------- */
3181
3182
0
bool Ntop::generateWebAuthnChallenge(char* challenge_b64, size_t len) const {
3183
0
  uint8_t raw[32];
3184
0
  if (RAND_bytes(raw, sizeof(raw)) != 1) return false;
3185
0
  std::string enc = webauthn_b64url_encode(raw, sizeof(raw));
3186
0
  strncpy(challenge_b64, enc.c_str(), len - 1);
3187
0
  challenge_b64[len - 1] = '\0';
3188
0
  return true;
3189
0
}
3190
3191
/* ---------------------------------------- */
3192
3193
bool Ntop::createWebAuthnPendingToken(const char* username, const char* referer,
3194
                                       char* token, size_t token_len,
3195
                                       char* challenge_b64,
3196
0
                                       size_t challenge_len) const {
3197
0
  if (!generateWebAuthnChallenge(challenge_b64, challenge_len)) return false;
3198
3199
0
  char rand_str[64], tmp_token[33];
3200
0
  srand((int)time(0) ^ (int)getpid());
3201
0
  snprintf(rand_str, sizeof(rand_str), "%d%s", rand(), username);
3202
0
  mg_md5(tmp_token, rand_str, NULL);
3203
0
  strncpy(token, tmp_token, token_len - 1);
3204
0
  token[token_len - 1] = '\0';
3205
3206
0
  char key[128], val[768];
3207
0
  snprintf(key, sizeof(key), "%s.%s", WEBAUTHN_PENDING_PREFIX, token);
3208
0
  snprintf(val, sizeof(val), "%s|%s|%s", username,
3209
0
           referer ? referer : "/", challenge_b64);
3210
0
  return (ntop->getRedis()->set(key, val, WEBAUTHN_PENDING_TTL) >= 0);
3211
0
}
3212
3213
/* ---------------------------------------- */
3214
3215
bool Ntop::getWebAuthnPendingToken(const char* token, char* username,
3216
                                    size_t username_len, char* referer,
3217
                                    size_t referer_len, char* challenge_b64,
3218
0
                                    size_t challenge_len) const {
3219
0
  char key[128], val[768];
3220
0
  snprintf(key, sizeof(key), "%s.%s", WEBAUTHN_PENDING_PREFIX, token);
3221
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) < 0) return false;
3222
3223
  /* val = "username|referer|challenge" */
3224
0
  char* p1 = strchr(val, '|');
3225
0
  if (!p1) return false;
3226
0
  *p1 = '\0';
3227
0
  char* p2 = strchr(p1 + 1, '|');
3228
0
  if (!p2) return false;
3229
0
  *p2 = '\0';
3230
3231
0
  strncpy(username, val, username_len - 1);
3232
0
  username[username_len - 1] = '\0';
3233
0
  strncpy(referer, p1 + 1, referer_len - 1);
3234
0
  referer[referer_len - 1] = '\0';
3235
0
  strncpy(challenge_b64, p2 + 1, challenge_len - 1);
3236
0
  challenge_b64[challenge_len - 1] = '\0';
3237
0
  return true;
3238
0
}
3239
3240
/* ---------------------------------------- */
3241
3242
0
void Ntop::deleteWebAuthnPendingToken(const char* token) const {
3243
0
  char key[128];
3244
0
  snprintf(key, sizeof(key), "%s.%s", WEBAUTHN_PENDING_PREFIX, token);
3245
0
  ntop->getRedis()->del(key);
3246
0
}
3247
3248
/* ---------------------------------------- */
3249
3250
0
bool Ntop::isWebAuthnEnabled(const char* username) const {
3251
0
  return (getWebAuthnCredentialCount(username) > 0);
3252
0
}
3253
3254
/* ---------------------------------------- */
3255
3256
0
int Ntop::getWebAuthnCredentialCount(const char* username) const {
3257
0
  char key[CONST_MAX_LEN_REDIS_KEY], val[16];
3258
0
  snprintf(key, sizeof(key), CONST_STR_USER_WEBAUTHN_CRED_COUNT, username);
3259
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) < 0) return 0;
3260
0
  int n = atoi(val);
3261
0
  return (n > 0 && n <= WEBAUTHN_MAX_CREDS) ? n : 0;
3262
0
}
3263
3264
/* Credential storage format (pipe-separated):
3265
 *   <cred_id_b64url>|<pk_x_hex>|<pk_y_hex>|<sign_count>|<name> */
3266
3267
0
static void webauthn_bytes_to_hex(const uint8_t* b, size_t len, char* hex) {
3268
0
  for (size_t i = 0; i < len; i++)
3269
0
    snprintf(hex + i * 2, 3, "%02x", b[i]);
3270
0
}
3271
3272
0
static bool webauthn_hex_to_bytes(const char* hex, uint8_t* out, size_t out_len) {
3273
0
  size_t hlen = strlen(hex);
3274
0
  if (hlen != out_len * 2) return false;
3275
0
  for (size_t i = 0; i < out_len; i++) {
3276
0
    char byte_str[3] = { hex[i*2], hex[i*2+1], '\0' };
3277
0
    char* end;
3278
0
    out[i] = (uint8_t)strtoul(byte_str, &end, 16);
3279
0
    if (end != byte_str + 2) return false;
3280
0
  }
3281
0
  return true;
3282
0
}
3283
3284
bool Ntop::getWebAuthnCredential(const char* username, int idx,
3285
                                   char* cred_id_b64, size_t cred_id_len,
3286
                                   uint8_t* pk_x, uint8_t* pk_y,
3287
                                   uint32_t* sign_count,
3288
0
                                   char* name, size_t name_len) const {
3289
0
  char key[CONST_MAX_LEN_REDIS_KEY], val[1024];
3290
0
  snprintf(key, sizeof(key), CONST_STR_USER_WEBAUTHN_CRED, username, idx);
3291
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) < 0) return false;
3292
3293
  /* Parse: cred_id|pk_x_hex|pk_y_hex|sign_count|name */
3294
0
  char* fields[5];
3295
0
  char* p = val;
3296
0
  for (int f = 0; f < 5; f++) {
3297
0
    fields[f] = p;
3298
0
    if (f < 4) {
3299
0
      char* sep = strchr(p, '|');
3300
0
      if (!sep) return false;
3301
0
      *sep = '\0';
3302
0
      p = sep + 1;
3303
0
    }
3304
0
  }
3305
0
  strncpy(cred_id_b64, fields[0], cred_id_len - 1);
3306
0
  cred_id_b64[cred_id_len - 1] = '\0';
3307
0
  if (!webauthn_hex_to_bytes(fields[1], pk_x, 32)) return false;
3308
0
  if (!webauthn_hex_to_bytes(fields[2], pk_y, 32)) return false;
3309
0
  *sign_count = (uint32_t)strtoul(fields[3], NULL, 10);
3310
0
  strncpy(name, fields[4], name_len - 1);
3311
0
  name[name_len - 1] = '\0';
3312
0
  return true;
3313
0
}
3314
3315
/* ---------------------------------------- */
3316
3317
bool Ntop::storeWebAuthnCredential(const char* username,
3318
                                     const char* cred_id_b64,
3319
                                     const uint8_t* pk_x, const uint8_t* pk_y,
3320
                                     uint32_t sign_count,
3321
0
                                     const char* name) const {
3322
0
  int n = getWebAuthnCredentialCount(username);
3323
0
  if (n >= WEBAUTHN_MAX_CREDS) return false; /* Limit reached */
3324
3325
0
  char x_hex[65], y_hex[65];
3326
0
  webauthn_bytes_to_hex(pk_x, 32, x_hex);
3327
0
  webauthn_bytes_to_hex(pk_y, 32, y_hex);
3328
3329
0
  char key[CONST_MAX_LEN_REDIS_KEY], val[1024];
3330
0
  snprintf(key, sizeof(key), CONST_STR_USER_WEBAUTHN_CRED, username, n);
3331
0
  snprintf(val, sizeof(val), "%s|%s|%s|%u|%s",
3332
0
           cred_id_b64, x_hex, y_hex, sign_count, name ? name : "Passkey");
3333
0
  if (ntop->getRedis()->set(key, val, 0) < 0) return false;
3334
3335
0
  char cnt_key[CONST_MAX_LEN_REDIS_KEY], cnt_val[16];
3336
0
  snprintf(cnt_key, sizeof(cnt_key), CONST_STR_USER_WEBAUTHN_CRED_COUNT, username);
3337
0
  snprintf(cnt_val, sizeof(cnt_val), "%d", n + 1);
3338
0
  return (ntop->getRedis()->set(cnt_key, cnt_val, 0) >= 0);
3339
0
}
3340
3341
/* ---------------------------------------- */
3342
3343
bool Ntop::deleteWebAuthnCredential(const char* username,
3344
0
                                     const char* cred_id_b64) const {
3345
0
  int n = getWebAuthnCredentialCount(username);
3346
0
  int found = -1;
3347
0
  for (int i = 0; i < n; i++) {
3348
0
    char id[512]; uint8_t x[32], y[32]; uint32_t sc; char nm[64];
3349
0
    if (getWebAuthnCredential(username, i, id, sizeof(id), x, y, &sc,
3350
0
                              nm, sizeof(nm)) &&
3351
0
        strcmp(id, cred_id_b64) == 0) {
3352
0
      found = i;
3353
0
      break;
3354
0
    }
3355
0
  }
3356
0
  if (found < 0) return false;
3357
3358
0
  char key[CONST_MAX_LEN_REDIS_KEY];
3359
3360
  /* Swap with last entry if not already the last */
3361
0
  if (found < n - 1) {
3362
0
    char last_val[1024];
3363
0
    snprintf(key, sizeof(key), CONST_STR_USER_WEBAUTHN_CRED, username, n - 1);
3364
0
    if (ntop->getRedis()->get(key, last_val, sizeof(last_val)) < 0) return false;
3365
0
    char found_key[CONST_MAX_LEN_REDIS_KEY];
3366
0
    snprintf(found_key, sizeof(found_key), CONST_STR_USER_WEBAUTHN_CRED, username, found);
3367
0
    ntop->getRedis()->set(found_key, last_val, 0);
3368
0
  }
3369
3370
  /* Delete the last slot */
3371
0
  snprintf(key, sizeof(key), CONST_STR_USER_WEBAUTHN_CRED, username, n - 1);
3372
0
  ntop->getRedis()->del(key);
3373
3374
  /* Update count */
3375
0
  char cnt_key[CONST_MAX_LEN_REDIS_KEY], cnt_val[16];
3376
0
  snprintf(cnt_key, sizeof(cnt_key), CONST_STR_USER_WEBAUTHN_CRED_COUNT, username);
3377
0
  snprintf(cnt_val, sizeof(cnt_val), "%d", n - 1);
3378
0
  ntop->getRedis()->set(cnt_key, cnt_val, 0);
3379
0
  return true;
3380
0
}
3381
3382
/* ---------------------------------------- */
3383
3384
bool Ntop::getWebAuthnCredentialsJSON(const char* username,
3385
0
                                       char* json_out, size_t len) const {
3386
0
  int n = getWebAuthnCredentialCount(username);
3387
0
  std::string out = "[";
3388
0
  for (int i = 0; i < n; i++) {
3389
0
    char id[512]; uint8_t x[32], y[32]; uint32_t sc; char nm[64];
3390
0
    if (!getWebAuthnCredential(username, i, id, sizeof(id), x, y, &sc,
3391
0
                               nm, sizeof(nm)))
3392
0
      continue;
3393
0
    if (out.size() > 1) out += ",";
3394
0
    char entry[768];
3395
0
    snprintf(entry, sizeof(entry),
3396
0
             "{\"id\":\"%s\",\"name\":\"%s\",\"sign_count\":%u}", id, nm, sc);
3397
0
    out += entry;
3398
0
  }
3399
0
  out += "]";
3400
0
  strncpy(json_out, out.c_str(), len - 1);
3401
0
  json_out[len - 1] = '\0';
3402
0
  return true;
3403
0
}
3404
3405
/* ---------------------------------------- */
3406
3407
bool Ntop::verifyWebAuthnAssertion(const char* username,
3408
                                    const char* cred_id_b64url,
3409
                                    const char* client_data_json_b64url,
3410
                                    const char* auth_data_b64url,
3411
                                    const char* signature_b64url,
3412
                                    const char* expected_challenge_b64url,
3413
                                    const char* expected_origin,
3414
0
                                    const char* rp_id) const {
3415
  /* 1. Decode base64url inputs */
3416
0
  uint8_t cdj_raw[8192]; int cdj_len;
3417
0
  uint8_t ad_raw[1024];  int ad_len;
3418
0
  uint8_t sig_raw[512];  int sig_len;
3419
3420
0
  cdj_len = webauthn_b64url_decode(client_data_json_b64url, cdj_raw,
3421
0
                                    (int)sizeof(cdj_raw) - 1);
3422
0
  ad_len  = webauthn_b64url_decode(auth_data_b64url, ad_raw, (int)sizeof(ad_raw));
3423
0
  sig_len = webauthn_b64url_decode(signature_b64url, sig_raw, (int)sizeof(sig_raw));
3424
0
  if (cdj_len <= 0 || ad_len <= 0 || sig_len <= 0) return false;
3425
0
  cdj_raw[cdj_len] = '\0'; /* null-terminate for JSON parsing */
3426
3427
  /* 2. Parse clientDataJSON */
3428
0
  char type_val[64], challenge_val[256], origin_val[256];
3429
0
  if (!json_get_str((char*)cdj_raw, "type",      type_val,      sizeof(type_val)))      return false;
3430
0
  if (!json_get_str((char*)cdj_raw, "challenge", challenge_val, sizeof(challenge_val))) return false;
3431
0
  if (!json_get_str((char*)cdj_raw, "origin",    origin_val,    sizeof(origin_val)))    return false;
3432
3433
0
  if (strcmp(type_val, "webauthn.get") != 0) return false;
3434
3435
  /* Compare challenges: decode both and compare bytes */
3436
0
  uint8_t ch_got[256], ch_exp[256];
3437
0
  int ch_got_len = webauthn_b64url_decode(challenge_val, ch_got, (int)sizeof(ch_got));
3438
0
  int ch_exp_len = webauthn_b64url_decode(expected_challenge_b64url, ch_exp, (int)sizeof(ch_exp));
3439
0
  if (ch_got_len <= 0 || ch_exp_len <= 0 || ch_got_len != ch_exp_len) return false;
3440
0
  if (memcmp(ch_got, ch_exp, ch_got_len) != 0) return false;
3441
3442
  /* Compare origins */
3443
0
  if (strcmp(origin_val, expected_origin) != 0) return false;
3444
3445
  /* 3. Verify rpIdHash */
3446
0
  uint8_t rp_hash[32];
3447
0
  SHA256((const uint8_t*)rp_id, strlen(rp_id), rp_hash);
3448
0
  if (ad_len < 37 || memcmp(ad_raw, rp_hash, 32) != 0) return false;
3449
3450
  /* 4. Check UP (User Present) flag */
3451
0
  if (!(ad_raw[32] & 0x01)) return false;
3452
3453
  /* 5. Extract signCount from authData */
3454
0
  uint32_t sign_count = ((uint32_t)ad_raw[33] << 24) | ((uint32_t)ad_raw[34] << 16) |
3455
0
                        ((uint32_t)ad_raw[35] << 8) | ad_raw[36];
3456
3457
  /* 6. Find the credential by ID, saving pk and name for later */
3458
0
  int n = getWebAuthnCredentialCount(username);
3459
0
  uint8_t pk_x[32], pk_y[32];
3460
0
  uint32_t stored_sc = 0;
3461
0
  char found_name[64] = "Passkey";
3462
0
  bool found = false;
3463
0
  int found_idx = -1;
3464
0
  for (int i = 0; i < n && !found; i++) {
3465
0
    char id[512]; uint8_t x[32], y[32]; uint32_t sc; char nm[64];
3466
0
    if (getWebAuthnCredential(username, i, id, sizeof(id), x, y, &sc,
3467
0
                              nm, sizeof(nm)) &&
3468
0
        strcmp(id, cred_id_b64url) == 0) {
3469
0
      memcpy(pk_x, x, 32); memcpy(pk_y, y, 32);
3470
0
      stored_sc = sc;
3471
0
      found_idx = i;
3472
0
      strncpy(found_name, nm, sizeof(found_name) - 1);
3473
0
      found_name[sizeof(found_name) - 1] = '\0';
3474
0
      found = true;
3475
0
    }
3476
0
  }
3477
0
  if (!found) return false;
3478
3479
  /* Sign count check: reject if stored > 0 and new <= stored (replay) */
3480
0
  if (stored_sc > 0 && sign_count <= stored_sc) return false;
3481
3482
  /* 7. Verify ECDSA signature: msg = authData || SHA256(clientDataJSON) */
3483
0
  uint8_t cdj_hash[32];
3484
0
  SHA256(cdj_raw, (size_t)cdj_len, cdj_hash);
3485
3486
0
  std::vector<uint8_t> msg((size_t)ad_len + 32);
3487
0
  memcpy(msg.data(), ad_raw, (size_t)ad_len);
3488
0
  memcpy(msg.data() + ad_len, cdj_hash, 32);
3489
3490
0
  if (!verify_ecdsa_p256(pk_x, pk_y, msg.data(), msg.size(), sig_raw, sig_len))
3491
0
    return false;
3492
3493
  /* 8. Update signCount in Redis using the saved name */
3494
0
  char key[CONST_MAX_LEN_REDIS_KEY], val[1024];
3495
0
  char x_hex[65], y_hex[65];
3496
0
  webauthn_bytes_to_hex(pk_x, 32, x_hex);
3497
0
  webauthn_bytes_to_hex(pk_y, 32, y_hex);
3498
0
  snprintf(key, sizeof(key), CONST_STR_USER_WEBAUTHN_CRED, username, found_idx);
3499
0
  snprintf(val, sizeof(val), "%s|%s|%s|%u|%s",
3500
0
           cred_id_b64url, x_hex, y_hex, sign_count, found_name);
3501
0
  ntop->getRedis()->set(key, val, 0);
3502
3503
0
  return true;
3504
0
}
3505
3506
/* ---------------------------------------- */
3507
3508
bool Ntop::verifyAndStoreWebAuthnRegistration(
3509
    const char* username, const char* cred_name,
3510
    const char* cred_id_b64url, const char* client_data_json_b64url,
3511
    const char* attestation_obj_b64url, const char* expected_challenge_b64url,
3512
0
    const char* expected_origin, const char* rp_id) const {
3513
3514
  /* 1. Decode inputs */
3515
0
  uint8_t cdj_raw[8192]; int cdj_len;
3516
0
  uint8_t ao_raw[8192];  int ao_len;
3517
3518
0
  cdj_len = webauthn_b64url_decode(client_data_json_b64url, cdj_raw,
3519
0
                                    (int)sizeof(cdj_raw) - 1);
3520
0
  ao_len  = webauthn_b64url_decode(attestation_obj_b64url, ao_raw,
3521
0
                                    (int)sizeof(ao_raw));
3522
0
  if (cdj_len <= 0 || ao_len <= 0) return false;
3523
0
  cdj_raw[cdj_len] = '\0';
3524
3525
  /* 2. Verify clientDataJSON */
3526
0
  char type_val[64], challenge_val[256], origin_val[256];
3527
0
  if (!json_get_str((char*)cdj_raw, "type",      type_val,      sizeof(type_val)))      return false;
3528
0
  if (!json_get_str((char*)cdj_raw, "challenge", challenge_val, sizeof(challenge_val))) return false;
3529
0
  if (!json_get_str((char*)cdj_raw, "origin",    origin_val,    sizeof(origin_val)))    return false;
3530
0
  if (strcmp(type_val, "webauthn.create") != 0) return false;
3531
3532
0
  uint8_t ch_got[256], ch_exp[256];
3533
0
  int ch_got_len = webauthn_b64url_decode(challenge_val, ch_got, (int)sizeof(ch_got));
3534
0
  int ch_exp_len = webauthn_b64url_decode(expected_challenge_b64url, ch_exp, (int)sizeof(ch_exp));
3535
0
  if (ch_got_len <= 0 || ch_exp_len <= 0 || ch_got_len != ch_exp_len) return false;
3536
0
  if (memcmp(ch_got, ch_exp, ch_got_len) != 0) return false;
3537
0
  if (strcmp(origin_val, expected_origin) != 0) return false;
3538
3539
  /* 3. Extract authData from attestationObject */
3540
0
  const uint8_t* auth_data;
3541
0
  size_t auth_data_len;
3542
0
  if (!extract_auth_data(ao_raw, (size_t)ao_len, &auth_data, &auth_data_len))
3543
0
    return false;
3544
3545
  /* 4. Parse authenticatorData */
3546
0
  wa_auth_data ad;
3547
0
  if (!parse_auth_data(auth_data, auth_data_len, &ad)) return false;
3548
0
  if (!ad.has_cred) return false;
3549
3550
  /* 5. Verify rpIdHash */
3551
0
  uint8_t rp_hash[32];
3552
0
  SHA256((const uint8_t*)rp_id, strlen(rp_id), rp_hash);
3553
0
  if (memcmp(ad.rp_id_hash, rp_hash, 32) != 0) return false;
3554
3555
  /* 6. Check UP flag */
3556
0
  if (!(ad.flags & 0x01)) return false;
3557
3558
  /* 7. Store the credential (cred_id comes from the client as b64url) */
3559
0
  return storeWebAuthnCredential(username, cred_id_b64url,
3560
0
                                 ad.pk_x, ad.pk_y, ad.sign_count,
3561
0
                                 cred_name && cred_name[0] ? cred_name : "Passkey");
3562
0
}
3563
3564
/* ******************************************* */
3565
3566
0
bool Ntop::isCaptivePortalUser(const char* username) {
3567
0
  char key[64], val[64];
3568
3569
0
  snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
3570
3571
0
  if ((ntop->getRedis()->get(key, val, sizeof(val)) >= 0) &&
3572
0
      (!strcmp(val, CONST_USER_GROUP_CAPTIVE_PORTAL))) {
3573
0
    return (true);
3574
0
  }
3575
3576
0
  return (false);
3577
0
}
3578
3579
/* ******************************************* */
3580
3581
0
bool Ntop::deleteUser(char* username) {
3582
0
  char user_id_buf[8];
3583
0
  char key[64];
3584
3585
0
  users_m.lock(__FILE__, __LINE__);
3586
3587
0
  if (!existsUser(username)) {
3588
0
    users_m.unlock(__FILE__, __LINE__);
3589
0
    return (false);
3590
0
  }
3591
3592
0
  snprintf(key, sizeof(key), CONST_STR_USER_ID, username);
3593
3594
  /* Dispose the currently assigned user id so that it can be recycled */
3595
0
  if ((ntop->getRedis()->get(key, user_id_buf, sizeof(user_id_buf)) >= 0)) {
3596
0
    ntop->getRedis()->srem(PREF_NTOP_USER_IDS, user_id_buf);
3597
0
  }
3598
3599
0
  snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username);
3600
0
  ntop->getRedis()->del(key);
3601
3602
0
  snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
3603
0
  ntop->getRedis()->del(key);
3604
3605
0
  snprintf(key, sizeof(key), CONST_STR_USER_ID, username);
3606
0
  ntop->getRedis()->del(key);
3607
3608
0
  snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username);
3609
0
  ntop->getRedis()->del(key);
3610
3611
0
  snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
3612
0
  ntop->getRedis()->del(key);
3613
3614
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username);
3615
0
  ntop->getRedis()->del(key);
3616
3617
0
  snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, username);
3618
0
  ntop->getRedis()->del(key);
3619
3620
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
3621
0
  ntop->getRedis()->del(key);
3622
3623
0
  snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username);
3624
0
  ntop->getRedis()->del(key);
3625
3626
0
  snprintf(key, sizeof(key), CONST_STR_USER_THEME, username);
3627
0
  ntop->getRedis()->del(key);
3628
3629
0
  snprintf(key, sizeof(key), CONST_STR_USER_DATE_FORMAT, username);
3630
0
  ntop->getRedis()->del(key);
3631
3632
  /*
3633
     Delete the API Token, first from the hash of all tokens,
3634
     then from the user
3635
  */
3636
0
  char api_token[NTOP_SESSION_ID_LENGTH];
3637
0
  if (getUserAPIToken(username, api_token, sizeof(api_token))) {
3638
0
    ntop->getRedis()->hashDel(NTOPNG_API_TOKEN_PREFIX, api_token);
3639
0
  }
3640
3641
0
  snprintf(key, sizeof(key), CONST_STR_USER_API_TOKEN, username);
3642
0
  ntop->getRedis()->del(key);
3643
3644
0
  users_m.unlock(__FILE__, __LINE__);
3645
3646
0
  return true;
3647
0
}
3648
3649
/* ******************************************* */
3650
3651
0
bool Ntop::getUserHostPool(char* username, u_int16_t* host_pool_id) {
3652
0
  char key[64], val[64];
3653
3654
0
  snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID,
3655
0
           username ? username : "");
3656
0
  if (ntop->getRedis()->get(key, val, sizeof(val)) >= 0) {
3657
0
    if (host_pool_id) *host_pool_id = atoi(val);
3658
0
    return true;
3659
0
  }
3660
3661
0
  if (host_pool_id) *host_pool_id = NO_HOST_POOL_ID;
3662
0
  return false;
3663
0
}
3664
3665
/* ******************************************* */
3666
3667
bool Ntop::getUserAllowedIfname(const char* username, char* buf,
3668
0
                                size_t buflen) const {
3669
0
  char key[64];
3670
3671
0
  snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME,
3672
0
           username ? username : "");
3673
3674
0
  if (ntop->getRedis()->get(key, buf, buflen) >= 0) return true;
3675
3676
0
  return false;
3677
0
}
3678
3679
/* ******************************************* */
3680
3681
bool Ntop::getUserAPIToken(const char* username, char* buf,
3682
0
                           size_t buflen) const {
3683
0
  char key[CONST_MAX_LEN_REDIS_KEY];
3684
3685
0
  snprintf(key, sizeof(key), CONST_STR_USER_API_TOKEN, username);
3686
3687
0
  if (ntop->getRedis()->get(key, buf, buflen) >= 0) return true;
3688
3689
0
  return false;
3690
0
}
3691
3692
/* ******************************************* */
3693
3694
100
void Ntop::fixPath(char* str, bool replaceDots) {
3695
5.28k
  for (int i = 0; str[i] != '\0'; i++) {
3696
#ifdef WIN32
3697
    /*
3698
      Allowed windows path and file characters:
3699
      https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#win32_file_namespaces
3700
    */
3701
    if (str[i] == '/')
3702
      str[i] = '\\';
3703
    else if (str[i] == '\\')
3704
      continue;
3705
    else if ((i == 1) && (str[i] == ':'))  // c:\\...
3706
      continue;
3707
    else if (str[i] == ':' || str[i] == '"' || str[i] == '|' || str[i] == '?' ||
3708
             str[i] == '*')
3709
      str[i] = '_';
3710
#endif
3711
3712
5.18k
    if (replaceDots) {
3713
5.18k
      if ((i > 0) && (str[i] == '.') && (str[i - 1] == '.')) {
3714
        // ntop->getTrace()->traceEvent(TRACE_WARNING, "Invalid path detected
3715
        // %s", str);
3716
0
        str[i - 1] = '_', str[i] = '_'; /* Invalidate the path */
3717
0
      }
3718
5.18k
    }
3719
5.18k
  }
3720
100
}
3721
3722
/* ******************************************* */
3723
3724
12
char* Ntop::getValidPath(char* __path) {
3725
12
  char _path[MAX_PATH + 8];
3726
12
  struct stat buf;
3727
3728
#ifdef WIN32
3729
  const char* install_dir = (const char*)get_install_dir();
3730
#endif
3731
12
  bool has_drive_colon = 0;
3732
3733
12
  if (strncmp(__path, "./", 2) == 0) {
3734
0
    snprintf(_path, sizeof(_path), "%s/%s", startup_dir, &__path[2]);
3735
0
    fixPath(_path);
3736
3737
0
    if (stat(_path, &buf) == 0) {
3738
0
      free(__path);
3739
0
      return (strdup(_path));
3740
0
    }
3741
0
  }
3742
3743
#ifdef WIN32
3744
  has_drive_colon =
3745
      (isalpha((int)__path[0]) &&
3746
       (__path[1] == ':' && (__path[2] == '\\' || __path[2] == '/')));
3747
#endif
3748
3749
12
  if ((__path[0] == '/') || (__path[0] == '\\') || has_drive_colon) {
3750
    /* Absolute paths */
3751
3752
12
    if (stat(__path, &buf) == 0) {
3753
12
      return (__path);
3754
12
    }
3755
12
  } else
3756
0
    snprintf(_path, MAX_PATH, "%s", __path);
3757
3758
  /* relative paths */
3759
0
  for (int i = 0; i < (int)COUNT_OF(dirs); i++) {
3760
0
    if (dirs[i]
3761
        /*
3762
           Ignore / as when you start ntopng as a
3763
           service you might have /scripts or /httpdocs
3764
           on your filesystem fooling ntopng
3765
           initialization and thus breaking averything
3766
        */
3767
0
        && strcmp(dirs[i], "/")) {
3768
0
      char path[2 * MAX_PATH];
3769
3770
0
      snprintf(path, sizeof(path), "%s/%s", dirs[i], _path);
3771
0
      fixPath(path);
3772
3773
0
      if (stat(path, &buf) == 0) {
3774
0
        free(__path);
3775
0
        return (strdup(path));
3776
0
      }
3777
0
    }
3778
0
  }
3779
3780
0
  free(__path);
3781
0
  return (strdup(""));
3782
0
}
3783
3784
/* ******************************************* */
3785
3786
0
void Ntop::daemonize() {
3787
0
#ifndef WIN32
3788
0
  int childpid;
3789
3790
0
  ntop->getTrace()->traceEvent(TRACE_NORMAL,
3791
0
                               "Parent process is exiting (this is normal)");
3792
3793
0
  signal(SIGPIPE, SIG_IGN);
3794
0
  signal(SIGHUP, SIG_IGN);
3795
  /*
3796
    IMPORTANT
3797
3798
    SIGCHLD should NOT be masked as otherwise
3799
    with popen()/pclose() we receive an error
3800
    when closing the pipe on FreeBSD
3801
3802
    signal(SIGCHLD, SIG_IGN);
3803
  */
3804
0
  signal(SIGQUIT, SIG_IGN);
3805
3806
0
  if ((childpid = fork()) < 0)
3807
0
    ntop->getTrace()->traceEvent(
3808
0
        TRACE_ERROR, "Occurred while daemonizing (errno=%d)", errno);
3809
0
  else {
3810
0
    if (!childpid) {
3811
      /* child */
3812
0
      int rc;
3813
3814
      // ntop->getTrace()->traceEvent(TRACE_NORMAL, "Bye bye: I'm becoming a
3815
      // daemon...");
3816
0
      rc = chdir("/");
3817
0
      if (rc != 0)
3818
0
        ntop->getTrace()->traceEvent(TRACE_ERROR,
3819
0
                                     "Error while moving to / directory");
3820
3821
0
      setsid(); /* detach from the terminal */
3822
3823
0
      fclose(stdin);
3824
0
      fclose(stdout);
3825
      /* fclose(stderr); */
3826
3827
      /*
3828
       * clear any inherited file mode creation mask
3829
       */
3830
      // umask(0);
3831
3832
      /*
3833
       * Use line buffered stdout
3834
       */
3835
      /* setlinebuf (stdout); */
3836
0
      setvbuf(stdout, (char*)NULL, _IOLBF, 0);
3837
0
    } else /* father */
3838
0
      exit(0);
3839
0
  }
3840
0
#endif
3841
0
}
3842
3843
/* ******************************************* */
3844
3845
4
void Ntop::setLocalNetworks(char* _nets) {
3846
4
  char* nets;
3847
4
  u_int len;
3848
3849
4
  if (_nets == NULL) return;
3850
3851
4
  len = (u_int)strlen(_nets);
3852
3853
4
  if ((len > 2) && (_nets[0] == '"') && (_nets[len - 1] == '"')) {
3854
0
    nets = strdup(&_nets[1]);
3855
0
    nets[len - 2] = '\0';
3856
0
  } else
3857
4
    nets = strdup(_nets);
3858
3859
4
  addLocalNetworkList(nets);
3860
4
  free(nets);
3861
4
};
3862
3863
/* ******************************************* */
3864
3865
0
NetworkInterface* Ntop::getInterfaceById(int if_id) {
3866
0
  if (if_id == SYSTEM_INTERFACE_ID) return (system_interface);
3867
3868
0
  for (int i = 0; i < num_defined_interfaces; i++) {
3869
0
    if (!iface[i]->isEnabled()) continue;
3870
0
    if (iface[i] && iface[i]->get_id() == if_id) return (iface[i]);
3871
0
  }
3872
3873
0
  return (NULL);
3874
0
}
3875
3876
/* ******************************************* */
3877
3878
0
bool Ntop::isExistingInterface(const char* name) const {
3879
0
  if (name == NULL) return (false);
3880
3881
0
  for (int i = 0; i < num_defined_interfaces; i++) {
3882
0
    if (!iface[i]->isEnabled()) continue;
3883
0
    if (!strcmp(iface[i]->get_name(), name)) return (true);
3884
0
  }
3885
3886
0
  if (!strcmp(name, getSystemInterface()->get_name())) return (true);
3887
3888
0
  return (false);
3889
0
}
3890
3891
/* ******************************************* */
3892
3893
0
NetworkInterface* Ntop::getNetworkInterface(const char* name, lua_State* vm) {
3894
0
  char allowed_ifname[MAX_INTERFACE_NAME_LEN] = {0};
3895
0
  char* bad_num = NULL;
3896
0
  int if_id;
3897
3898
0
  if (vm && getInterfaceAllowed(vm, allowed_ifname)) {
3899
0
    ntop->getTrace()->traceEvent(
3900
0
        TRACE_DEBUG, "Forcing allowed interface. [requested: %s][selected: %s]",
3901
0
        name, allowed_ifname);
3902
0
    return getNetworkInterface(allowed_ifname);
3903
0
  }
3904
3905
0
  if (name == NULL) return (NULL);
3906
3907
  /* This method accepts both interface names or Ids.
3908
   * Due to bad Lua number formatting, a float number may be received. */
3909
0
  if_id = strtof(name, &bad_num);
3910
3911
0
  if ((if_id == SYSTEM_INTERFACE_ID) || !strcmp(name, SYSTEM_INTERFACE_NAME))
3912
0
    return (getSystemInterface());
3913
3914
0
  if ((bad_num == NULL) || (*bad_num == '\0')) {
3915
    /* name is a number */
3916
0
    return (getInterfaceById(if_id));
3917
0
  }
3918
3919
  /* if here, name is a string */
3920
0
  for (int i = 0; i < num_defined_interfaces; i++) {
3921
0
    if (!iface[i]->isEnabled()) continue;
3922
0
    if (!strcmp(name, iface[i]->get_name())) {
3923
0
      NetworkInterface* ret_iface =
3924
0
          isInterfaceAllowed(vm, iface[i]->get_name()) ? iface[i] : NULL;
3925
3926
0
      if (ret_iface) return (ret_iface);
3927
0
    }
3928
0
  }
3929
3930
0
  return (NULL);
3931
0
};
3932
3933
/* ******************************************* */
3934
3935
0
int Ntop::getInterfaceIdByName(lua_State* vm, const char* name) {
3936
0
  NetworkInterface* res = getNetworkInterface(name, vm);
3937
3938
0
  if (res) return res->get_id();
3939
3940
0
  return (-1);
3941
0
}
3942
3943
/* ****************************************** */
3944
3945
/* NOTE: the interface is deleted when this method returns false */
3946
0
bool Ntop::registerInterface(NetworkInterface* _if) {
3947
0
  bool rv = true;
3948
3949
  /* Needed as can be called concurrently by
3950
   * NetworkInterface::registerSubInterface */
3951
0
  m.lock(__FILE__, __LINE__);
3952
3953
0
  for (int i = 0; i < num_defined_interfaces; i++) {
3954
0
    if (strcmp(iface[i]->get_name(), _if->get_name()) == 0) {
3955
0
      ntop->getTrace()->traceEvent(TRACE_WARNING,
3956
0
                                   "Skipping duplicated interface %s",
3957
0
                                   _if->get_description());
3958
3959
0
      rv = false;
3960
0
      goto out;
3961
0
    }
3962
0
  }
3963
3964
0
  if (num_defined_interfaces < MAX_NUM_DEFINED_INTERFACES) {
3965
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL,
3966
0
                                 "Registered interface '%s' [id: %d]",
3967
0
                                 _if->get_description(), _if->get_id());
3968
0
    iface[num_defined_interfaces++] = _if;
3969
3970
0
    rv = true;
3971
0
    goto out;
3972
0
  } else {
3973
0
    static bool too_many_interfaces_error = false;
3974
0
    if (!too_many_interfaces_error) {
3975
0
      ntop->getTrace()->traceEvent(TRACE_ERROR, "Too many interfaces defined");
3976
0
      too_many_interfaces_error = true;
3977
0
    }
3978
3979
0
    rv = false;
3980
0
    goto out;
3981
0
  }
3982
3983
0
out:
3984
0
  if (!rv) delete _if;
3985
3986
0
  m.unlock(__FILE__, __LINE__);
3987
3988
0
  return (rv);
3989
0
};
3990
3991
/* ******************************************* */
3992
3993
0
void Ntop::initInterface(NetworkInterface* _if, bool disable_dump) {
3994
  /* Initialization related to flow-dump */
3995
0
  if ((ntop->getPrefs()->do_dump_flows()
3996
0
#ifdef HAVE_ZMQ
3997
0
#ifndef HAVE_NEDGE
3998
0
       || ntop->get_export_interface()
3999
0
#endif
4000
0
#endif
4001
0
           ) &&
4002
0
      !disable_dump) {
4003
0
    _if->initFlowDump();
4004
0
  }
4005
4006
  /* Other initialization activities */
4007
0
  _if->initFlowChecksLoop();
4008
0
  _if->initHostChecksLoop();
4009
0
  _if->checkDisaggregationMode();
4010
0
}
4011
4012
/* *************************************** */
4013
4014
0
void Ntop::addToPool(char* host_or_mac, u_int16_t user_pool_id) {
4015
0
  char key[128], pool_buf[16];
4016
4017
#ifdef HOST_POOLS_DEBUG
4018
  ntop->getTrace()->traceEvent(TRACE_INFO,
4019
                               "Adding %s as host pool member [pool id: %i]",
4020
                               host_or_mac, user_pool_id);
4021
#endif
4022
4023
0
  snprintf(pool_buf, sizeof(pool_buf), "%u", user_pool_id);
4024
0
  snprintf(key, sizeof(key), HOST_POOL_MEMBERS_KEY, pool_buf);
4025
0
  ntop->getRedis()->sadd(key, host_or_mac); /* New member added */
4026
4027
0
  reloadHostPools();
4028
0
}
4029
4030
/* ******************************************* */
4031
4032
0
void Ntop::checkReloadHostPools() {
4033
0
  if (hostPoolsReloadInProgress /* Check if a reload has been requested */) {
4034
    /* Leave this BEFORE the actual swap and new allocation to guarantee changes
4035
     * are always seen */
4036
0
    hostPoolsReloadInProgress = false;
4037
4038
0
    for (int i = 0; i < get_num_interfaces(); i++) {
4039
0
      NetworkInterface* iface;
4040
4041
0
      if ((iface = ntop->getInterface(i)) != NULL) iface->reloadHostPools();
4042
0
    }
4043
0
  }
4044
0
}
4045
4046
/* ******************************************* */
4047
4048
0
u_int16_t Ntop::getNumberHostPools() {
4049
0
  u_int16_t pools_number = 0;
4050
0
  for (int i = 0; i < get_num_interfaces(); i++) {
4051
0
    NetworkInterface* iface;
4052
4053
0
    if ((iface = ntop->getInterface(i)) != NULL) {
4054
0
      pools_number = pools_number + iface->getNumberHostPools();
4055
0
      break;
4056
0
    }
4057
0
  }
4058
0
  return pools_number;
4059
0
}
4060
4061
/* ******************************************* */
4062
4063
0
u_int32_t Ntop::getNumberHostPoolsMembers() {
4064
0
  hostPoolsReloadInProgress = false;
4065
0
  u_int32_t max_members_number = 0;
4066
4067
0
  for (int i = 0; i < get_num_interfaces(); i++) {
4068
0
    NetworkInterface* iface;
4069
0
    u_int32_t pool_member_number = 0;
4070
4071
0
    if ((iface = ntop->getInterface(i)) != NULL)
4072
0
      pool_member_number = iface->getNumberHostPoolsMembers();
4073
4074
0
    if (pool_member_number > max_members_number)
4075
0
      max_members_number = pool_member_number;
4076
0
  }
4077
0
  return max_members_number;
4078
0
}
4079
4080
/* ******************************************* */
4081
4082
0
u_int8_t Ntop::getNumberProfiles() {
4083
0
  u_int8_t num_profiles = 0;
4084
#ifdef NTOPNG_PRO
4085
#ifndef HAVE_NEDGE
4086
#ifdef HAVE_NBPF
4087
  for (int i = 0; i < get_num_interfaces(); i++) {
4088
    NetworkInterface* iface;
4089
4090
    if ((iface = ntop->getInterface(i)) != NULL)
4091
      num_profiles = iface->getNumProfiles();
4092
  }
4093
#endif
4094
#endif
4095
#endif
4096
4097
0
  return num_profiles;
4098
0
}
4099
4100
/* ******************************************* */
4101
4102
4
void Ntop::checkReloadAlertExclusions() {
4103
#ifdef NTOPNG_PRO
4104
  if (alert_exclusions_shadow) { /* Dispose old memory if necessary */
4105
    delete alert_exclusions_shadow;
4106
    alert_exclusions_shadow = NULL;
4107
  }
4108
4109
  if (alertExclusionsReloadInProgress /* Check if a reload has been requested */
4110
      || !alert_exclusions /* Control groups are not allocated */) {
4111
    alertExclusionsReloadInProgress =
4112
        false; /* Leave this BEFORE the actual swap and new allocation to
4113
                  guarantee changes are always seen */
4114
4115
    alert_exclusions_shadow = alert_exclusions; /* Save the existing instance */
4116
    alert_exclusions =
4117
        new (std::nothrow) AlertExclusions(); /* Allocate a new instance */
4118
4119
    if (!alert_exclusions)
4120
      ntop->getTrace()->traceEvent(
4121
          TRACE_ERROR, "Unable to allocate memory for control groups.");
4122
  }
4123
#endif
4124
4
}
4125
4126
/* ******************************************* */
4127
4128
0
void Ntop::checkReloadFlowChecks() {
4129
0
  if(flowChecksReloadInProgress /* Reload requested from the UI upon configuration changes */) {
4130
0
    FlowChecksLoader *old,
4131
0
        *tmp_flow_checks_loader = new (std::nothrow) FlowChecksLoader();
4132
4133
0
    if (!tmp_flow_checks_loader) {
4134
0
      ntop->getTrace()->traceEvent(
4135
0
          TRACE_ERROR, "Unable to allocate memory for flow checks.");
4136
0
      return;
4137
0
    }
4138
4139
0
    tmp_flow_checks_loader->initialize();
4140
0
    old = flow_checks_loader;
4141
4142
    /* Pass the newly allocated loader to all interfaces so they will update
4143
     * their checks */
4144
0
    for (int i = 0; i < get_num_interfaces(); i++)
4145
0
      iface[i]->reloadFlowChecks(tmp_flow_checks_loader);
4146
4147
0
    flow_checks_loader = tmp_flow_checks_loader;
4148
4149
0
    if (old) {
4150
0
      sleep(2); /* Make sure nobody is using the old one */
4151
4152
0
      delete old;
4153
0
    }
4154
4155
0
    flowChecksReloadInProgress = false;
4156
0
  }
4157
0
}
4158
4159
/* ******************************************* */
4160
4161
0
void Ntop::checkReloadHostChecks() {
4162
0
  if(hostChecksReloadInProgress /* Reload requested from the UI upon configuration changes */) {
4163
0
    HostChecksLoader *old,
4164
0
        *tmp_host_checks_loader = new (std::nothrow) HostChecksLoader();
4165
4166
0
    if (!tmp_host_checks_loader) {
4167
0
      ntop->getTrace()->traceEvent(
4168
0
          TRACE_ERROR, "Unable to allocate memory for host checks.");
4169
0
      return;
4170
0
    }
4171
4172
0
    tmp_host_checks_loader->initialize();
4173
0
    old = host_checks_loader;
4174
4175
    /* Pass the newly allocated loader to all interfaces so they will update
4176
     * their checks */
4177
0
    for (int i = 0; i < get_num_interfaces(); i++)
4178
0
      iface[i]->reloadHostChecks(tmp_host_checks_loader);
4179
4180
0
    host_checks_loader = tmp_host_checks_loader;
4181
4182
0
    if (old) {
4183
0
      sleep(2); /* Make sure nobody is using the old one */
4184
4185
0
      delete old;
4186
0
    }
4187
4188
0
    hostChecksReloadInProgress = false;
4189
0
  }
4190
0
}
4191
4192
/* ******************************************* */
4193
4194
0
void Ntop::registerThread(const char* name, pthread_t id) {
4195
0
  Utils::setThreadName(name);
4196
4197
0
  ThreadInfo ti;
4198
0
  ti.name = name;
4199
0
  ti.last_cpu_ts.tv_sec = ti.last_cpu_ts.tv_nsec = 0;
4200
0
  ti.last_elapsed_ts.tv_sec = ti.last_elapsed_ts.tv_nsec = 0;
4201
4202
0
  threads_info_m.lock(__FILE__, __LINE__);
4203
0
  threads_info[id] = ti;
4204
0
  threads_info_m.unlock(__FILE__, __LINE__);
4205
0
}
4206
4207
/* ******************************************* */
4208
4209
0
void Ntop::lua_threadsInfo(lua_State* vm) {
4210
0
#if defined(__linux__)
4211
0
  std::map<pthread_t, ThreadInfo>::iterator it;
4212
0
  struct timespec elapsed_now;
4213
4214
0
  clock_gettime(CLOCK_MONOTONIC, &elapsed_now);
4215
4216
0
  threads_info_m.lock(__FILE__, __LINE__);
4217
4218
0
  for (it = threads_info.begin(); it != threads_info.end(); ++it) {
4219
0
    pthread_t tid = it->first;
4220
0
    ThreadInfo& ti = it->second;
4221
0
    clockid_t cpu_clock_id;
4222
4223
0
    if (pthread_getcpuclockid(tid, &cpu_clock_id) == 0) {
4224
0
      struct timespec cpu_now;
4225
4226
0
      if (clock_gettime(cpu_clock_id, &cpu_now) == 0) {
4227
0
        float utilization = 0;
4228
4229
0
        if (ti.last_elapsed_ts.tv_sec > 0 || ti.last_elapsed_ts.tv_nsec > 0) {
4230
0
          int64_t delta_cpu_ns =
4231
0
              ((int64_t)cpu_now.tv_sec - (int64_t)ti.last_cpu_ts.tv_sec) *
4232
0
                  1000000000 +
4233
0
              (cpu_now.tv_nsec - ti.last_cpu_ts.tv_nsec);
4234
0
          int64_t delta_elapsed_ns =
4235
0
              ((int64_t)elapsed_now.tv_sec -
4236
0
               (int64_t)ti.last_elapsed_ts.tv_sec) *
4237
0
                  1000000000 +
4238
0
              (elapsed_now.tv_nsec - ti.last_elapsed_ts.tv_nsec);
4239
0
          if (delta_elapsed_ns > 0)
4240
0
            utilization = (float)delta_cpu_ns * 100 / (float)delta_elapsed_ns;
4241
0
        }
4242
4243
0
        ti.last_cpu_ts = cpu_now;
4244
0
        ti.last_elapsed_ts = elapsed_now;
4245
4246
0
        u_int64_t total_ms =
4247
0
            (u_int64_t)cpu_now.tv_sec * 1000 + cpu_now.tv_nsec / 1000000;
4248
4249
0
        lua_pushstring(vm, ti.name.c_str());
4250
0
        lua_gettable(vm, -2);
4251
4252
0
        if (lua_istable(vm, -1)) {
4253
          /* If another thread with the same name was already added, sum it */
4254
0
          lua_getfield(vm, -1, "cpu_utilization_pct");
4255
0
          float existing_pct = (float)lua_tonumber(vm, -1);
4256
0
          lua_pop(vm, 1);
4257
0
          lua_pushnumber(vm, existing_pct + utilization);
4258
0
          lua_setfield(vm, -2, "cpu_utilization_pct");
4259
4260
0
          lua_getfield(vm, -1, "cpu_total_ms");
4261
0
          u_int64_t existing_ms = (u_int64_t)lua_tonumber(vm, -1);
4262
0
          lua_pop(vm, 1);
4263
0
          lua_pushnumber(vm, (lua_Number)(existing_ms + total_ms));
4264
0
          lua_setfield(vm, -2, "cpu_total_ms");
4265
4266
0
          lua_pop(vm, 1);
4267
0
        } else {
4268
0
          lua_pop(vm, 1);
4269
4270
0
          lua_newtable(vm);
4271
0
          lua_push_float_table_entry(vm, "cpu_utilization_pct", utilization);
4272
0
          lua_push_uint64_table_entry(vm, "cpu_total_ms", total_ms);
4273
4274
0
          lua_pushstring(vm, ti.name.c_str());
4275
0
          lua_insert(vm, -2);
4276
0
          lua_settable(vm, -3);
4277
0
        }
4278
0
      }
4279
0
    }
4280
0
  }
4281
4282
0
  threads_info_m.unlock(__FILE__, __LINE__);
4283
0
#endif
4284
0
}
4285
4286
/* ******************************************* */
4287
4288
/* Execute lightweigth tasks with high frequency */
4289
0
void Ntop::runHousekeepingTasks() {
4290
0
  checkReloadHostPools();
4291
4292
#ifdef NTOPNG_PRO
4293
  pro->runHousekeepingTasks();
4294
#endif
4295
0
}
4296
4297
/* ******************************************* */
4298
4299
/* Execute tasks periodically (5 sec freq)
4300
 * NOTE: the multiple isShutdown checks below are necessary to reduce the
4301
 * shutdown time */
4302
0
void Ntop::runPeriodicHousekeepingTasks() {
4303
0
  runHousekeepingTasks();
4304
4305
0
  checkReloadAlertExclusions();
4306
0
  checkReloadFlowChecks();
4307
0
  checkReloadHostChecks();
4308
4309
0
  for (int i = 0; i < get_num_interfaces(); i++) {
4310
0
    if (!iface[i]->isStartingUp()) {
4311
0
      iface[i]->runPeriodicHousekeepingTasks();
4312
0
      iface[i]->purgeQueuedIdleEntries();
4313
0
    }
4314
0
  }
4315
4316
0
  jobsQueue.idleTask();
4317
0
}
4318
4319
/* ******************************************* */
4320
4321
0
void Ntop::runShutdownTasks() {
4322
  /* Final shut down tasks for all interfaces */
4323
0
  for (int i = 0; i < num_defined_interfaces; i++) {
4324
0
    if (!iface[i]->isView()) iface[i]->runShutdownTasks();
4325
0
  }
4326
4327
0
  for (int i = 0; i < num_defined_interfaces; i++) {
4328
0
    if (iface[i]->isView()) iface[i]->runShutdownTasks();
4329
0
  }
4330
0
}
4331
4332
/* ******************************************* */
4333
4334
/*
4335
  Checks if all the activities are completed (e.g., all packets processed,
4336
  notifications sent) and possibly sends a shutdown signal to terminate. NOTE:
4337
  Only effective when ntopng is started with --shutdown-when-done. Without that
4338
  options ntopng keeps running and doesn't terminate.
4339
*/
4340
0
void Ntop::checkShutdownWhenDone() {
4341
0
  if (ntop->getPrefs()->shutdownWhenDone()) {
4342
0
    for (int i = 0; i < get_num_interfaces(); i++) {
4343
0
      NetworkInterface* iface = getInterface(i);
4344
4345
      /* Check all the interfaces reading from pcap files if they are done with
4346
       * their activities. */
4347
0
      if (iface->read_from_pcap_dump() && !iface->read_from_pcap_dump_done())
4348
        /* iface isn't done yet */
4349
0
        return;
4350
0
    }
4351
4352
    /* Here all interface reading from pcap files are done. */
4353
4354
0
    if (!recipients_are_empty()) {
4355
      /* Recipients are still processing notifications, wait until they're done.
4356
       */
4357
0
      ntop->getTrace()->traceEvent(TRACE_NORMAL,
4358
0
                                   "Waiting for pending notifications..");
4359
0
      return;
4360
0
    }
4361
4362
    /* When they are done, signal ntopng to shutdown */
4363
4364
    /* One extra housekeeping before executing tests (this assumes all flows
4365
     * have been walked) */
4366
0
    runPeriodicHousekeepingTasks();
4367
4368
0
    runShutdownTasks();
4369
4370
    /* Test Script (Runtime Analysis) */
4371
0
    if (ntop->getPrefs()->get_test_runtime_script_path()) {
4372
0
      const char* test_runtime_script_path =
4373
0
          ntop->getPrefs()->get_test_runtime_script_path();
4374
4375
      /* Execute as Bash script */
4376
0
      ntop->getTrace()->traceEvent(TRACE_NORMAL,
4377
0
                                   "> Running Runtime Script '%s'",
4378
0
                                   test_runtime_script_path);
4379
0
      sleep(10); /* Allow concurrent activities and flushing to complete before
4380
                    running the test */
4381
0
      Utils::exec(test_runtime_script_path);
4382
0
    }
4383
4384
    /* Perform shutdown operations on all active interfaces - this also flushes
4385
     * all active flows */
4386
0
    ntop->shutdownInterfaces();
4387
4388
    /* Make sure all flushed flows are also dumped to the database for post
4389
     * analysis (e.g. historical data) */
4390
4391
    /* Test Script (Post Analysis) */
4392
0
    if (ntop->getPrefs()->get_test_post_script_path()) {
4393
0
      const char* test_post_script_path =
4394
0
          ntop->getPrefs()->get_test_post_script_path();
4395
4396
0
      sleep(1); /* Give some time to alerts to get dequeued */
4397
4398
      /* Execute as Bash script */
4399
0
      ntop->getTrace()->traceEvent(TRACE_NORMAL, "> Running Post Script '%s'",
4400
0
                                   test_post_script_path);
4401
0
      Utils::exec(test_post_script_path);
4402
0
    }
4403
4404
0
    ntop->getGlobals()->shutdown();
4405
0
  }
4406
0
}
4407
4408
/* ******************************************* */
4409
4410
0
void Ntop::shutdownPeriodicActivities() {
4411
0
  if (pa) {
4412
0
    while (pa->isRunning()) sleep(1);
4413
4414
0
    delete pa;
4415
0
    pa = NULL;
4416
0
  }
4417
0
}
4418
4419
/* ******************************************* */
4420
4421
0
void Ntop::shutdownInterfaces() {
4422
  /* First, shutdown all view interfaces so they can release counters from the
4423
   * viewed interfaces */
4424
4425
0
  if (interfacesShuttedDown) return;
4426
4427
0
  for (int i = 0; i < num_defined_interfaces; i++) {
4428
0
    if (iface[i]->isView()) {
4429
0
      EthStats* stats = iface[i]->getStats();
4430
4431
0
      stats->print();
4432
0
      iface[i]->shutdown();
4433
0
      ntop->getTrace()->traceEvent(TRACE_NORMAL,
4434
0
                                   "Polling shut down [interface: %s]",
4435
0
                                   iface[i]->get_description());
4436
0
    }
4437
0
  }
4438
4439
  /* Now, shutdown all other non-view interfaces */
4440
0
  for (int i = 0; i < num_defined_interfaces; i++) {
4441
0
    if (!iface[i]->isView()) {
4442
0
      EthStats* stats = iface[i]->getStats();
4443
4444
0
      stats->print();
4445
0
      iface[i]->shutdown();
4446
0
      ntop->getTrace()->traceEvent(TRACE_NORMAL,
4447
0
                                   "Polling shut down [interface: %s]",
4448
0
                                   iface[i]->get_description());
4449
0
    }
4450
0
  }
4451
4452
0
  interfacesShuttedDown = true;
4453
0
}
4454
4455
/* ******************************************* */
4456
4457
0
void Ntop::shutdownAll() {
4458
0
  ThreadedActivity* shutdown_activity;
4459
4460
  /* Wait until currently executing periodic activities are completed,
4461
   * Periodic activites should not run during interfaces shutdown */
4462
0
  ntop->shutdownPeriodicActivities();
4463
4464
  /* Perform shutdown operations on all active interfaces,
4465
   * including purging all active flows, hosts, etc */
4466
0
  ntop->shutdownInterfaces();
4467
4468
0
  ntop->getTrace()->traceEvent(TRACE_NORMAL, "Executing shutdown script [%s]",
4469
0
                               SHUTDOWN_SCRIPT_PATH);
4470
4471
  /* Exec shutdown script before shutting down ntopng */
4472
0
  if ((shutdown_activity =
4473
0
           new (std::nothrow) ThreadedActivity(SHUTDOWN_SCRIPT_PATH))) {
4474
    /* Don't call run() as by the time the script will be run the delete below
4475
     * will free the memory */
4476
0
    shutdown_activity->runSystemScript(time(NULL));
4477
0
    delete shutdown_activity;
4478
0
  }
4479
4480
  /* Complete the shutdown */
4481
0
  ntop->getGlobals()->shutdown();
4482
4483
0
#ifndef WIN32
4484
  /*
4485
    PID file cannot be deleted as it is under `/var/run` which, in turn, is a
4486
    symlink to `/run`, which is not writable by user `ntopng`. As user `ntopng`
4487
    has no write privileges on `/run`, the PID file cannot be deleted from
4488
    inside this process. Deletion is performed as part of the ExecStopPost in
4489
    the systemd ntopng.service file
4490
  */
4491
#if 0
4492
  if(ntop->getPrefs()->get_pid_path() != NULL) {
4493
    int rc = unlink(ntop->getPrefs()->get_pid_path());
4494
4495
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "Deleted PID %s: [rc: %d][%s]",
4496
                                 ntop->getPrefs()->get_pid_path(),
4497
                                 rc, strerror(errno));
4498
  }
4499
#endif
4500
0
#endif
4501
4502
#ifdef HAVE_NEDGE
4503
  for (auto it = multicastForwarders.begin(); it != multicastForwarders.end();
4504
       ++it) {
4505
    (*it)->stop();
4506
  }
4507
#endif
4508
0
}
4509
4510
/* ******************************************* */
4511
4512
0
void Ntop::loadTrackers() {
4513
0
  FILE* fd;
4514
0
  char line[MAX_PATH];
4515
4516
0
  snprintf(line, sizeof(line), "%s/other/trackers.txt", prefs->get_docs_dir());
4517
4518
0
  if ((fd = fopen(line, "r")) != NULL) {
4519
0
    if ((trackers_automa = ndpi_init_automa()) == NULL) {
4520
0
      ntop->getTrace()->traceEvent(TRACE_WARNING,
4521
0
                                   "Unable to initialize trackers");
4522
0
      fclose(fd);
4523
0
      return;
4524
0
    }
4525
4526
0
    while (fgets(line, MAX_PATH, fd) != NULL) {
4527
0
      char* str = strdup(line);
4528
0
      if (str) ndpi_add_string_to_automa(trackers_automa, str);
4529
0
    }
4530
4531
0
    fclose(fd);
4532
0
    ndpi_finalize_automa(trackers_automa);
4533
0
  } else
4534
0
    ntop->getTrace()->traceEvent(TRACE_WARNING,
4535
0
                                 "Unable to load trackers file %s", line);
4536
0
}
4537
4538
/* ******************************************* */
4539
4540
0
bool Ntop::isATrackerHost(char* host) {
4541
0
  return trackers_automa && ndpi_match_string(trackers_automa, host) > 0;
4542
0
}
4543
4544
/* ******************************************* */
4545
4546
4
void Ntop::initAllowedProtocolPresets() {
4547
4
  struct ndpi_detection_module_struct* ndpi_struct =
4548
4
      ndpi_init_detection_module(NULL);
4549
4
  u_int num_protocols;
4550
4551
4
  if (ndpi_struct) {
4552
4
    ndpi_finalize_initialization(ndpi_struct);
4553
4
    num_protocols = ndpi_get_num_protocols(ndpi_struct);
4554
4
    ndpi_exit_detection_module(ndpi_struct);
4555
4
  } else
4556
0
    throw "Allocation error";
4557
4558
56
  for (u_int i = 0; i < device_max_type; i++) {
4559
52
    DeviceProtocolBitmask* b = getDeviceAllowedProtocols((DeviceType)i);
4560
4561
52
    ndpi_bitmask_alloc(&b->clientAllowed, num_protocols);
4562
52
    ndpi_bitmask_alloc(&b->serverAllowed, num_protocols);
4563
4564
52
    ndpi_bitmask_set_all(&b->clientAllowed);
4565
52
    ndpi_bitmask_set_all(&b->serverAllowed);
4566
52
  }
4567
4
}
4568
4569
/* ******************************************* */
4570
4571
void Ntop::refreshAllowedProtocolPresets(DeviceType device_type, bool client,
4572
0
                                         lua_State* L, int index) {
4573
0
  DeviceProtocolBitmask* b = getDeviceAllowedProtocols(device_type);
4574
4575
0
  lua_pushnil(L);
4576
4577
0
  if (b == NULL) return;
4578
4579
0
  if (client)
4580
0
    ndpi_bitmask_reset(&b->clientAllowed);
4581
0
  else
4582
0
    ndpi_bitmask_reset(&b->serverAllowed);
4583
4584
0
  while (lua_next(L, index) != 0) {
4585
0
    u_int key_proto = lua_tointeger(L, -2);
4586
0
    int t = lua_type(L, -1);
4587
4588
0
    if ((int)key_proto < 0) continue;
4589
4590
0
    switch (t) {
4591
0
      case LUA_TNUMBER: {
4592
0
        u_int value_action = lua_tointeger(L, -1);
4593
4594
0
        if (value_action) {
4595
0
          u_int32_t mapped_key_proto = ndpi_map_user_proto_id_to_ndpi_id(
4596
0
              iface[0]->get_ndpi_struct(), key_proto);
4597
4598
          /* ntop->getTrace()->traceEvent(TRACE_INFO, "%u -> %u", key_proto,
4599
           * mapped_key_proto); */
4600
4601
0
          if (client)
4602
0
            ndpi_bitmask_set(&b->clientAllowed, mapped_key_proto);
4603
0
          else
4604
0
            ndpi_bitmask_set(&b->serverAllowed, mapped_key_proto);
4605
0
        }
4606
0
      } break;
4607
4608
0
      default:
4609
0
        ntop->getTrace()->traceEvent(TRACE_ERROR,
4610
0
                                     "Internal error: type %d not handled", t);
4611
0
        break;
4612
0
    }
4613
4614
0
    lua_pop(L, 1);
4615
0
  }
4616
0
}
4617
4618
/* ******************************************* */
4619
4620
#ifdef HAVE_NEDGE
4621
bool Ntop::addIPToLRUMatches(u_int32_t client_ip, u_int16_t user_pool_id,
4622
                             char* label, char* ifname) {
4623
  for (int i = 0; i < num_defined_interfaces; i++) {
4624
    if (iface[i]->is_bridge_interface() &&
4625
        (strcmp(iface[i]->get_name(), ifname) == 0)) {
4626
      iface[i]->addIPToLRUMatches(client_ip, user_pool_id, label);
4627
      return true;
4628
    }
4629
  }
4630
4631
  return false;
4632
}
4633
4634
/* ******************************************* */
4635
4636
void Ntop::addToNotifiedInformativeCaptivePortal(u_int32_t client_ip) {
4637
  for (int i = 0; i < num_defined_interfaces; i++) {
4638
    if (iface[i]->is_bridge_interface()) /* TODO: handle multiple interfaces
4639
                                            separately */
4640
      iface[i]->addToNotifiedInformativeCaptivePortal(client_ip);
4641
  }
4642
}
4643
#endif
4644
4645
/* ******************************************* */
4646
4647
DeviceProtoStatus Ntop::getDeviceAllowedProtocolStatus(DeviceType dev_type,
4648
                                                       ndpi_protocol proto,
4649
                                                       u_int16_t pool_id,
4650
0
                                                       bool as_client) {
4651
  /* Check if this application protocol is allowd for the specified device type
4652
   */
4653
0
  DeviceProtocolBitmask* bitmask = getDeviceAllowedProtocols(dev_type);
4654
0
  struct ndpi_bitmask* direction_bitmask =
4655
0
      as_client ? (&bitmask->clientAllowed) : (&bitmask->serverAllowed);
4656
0
  u_int16_t master_proto = ndpi_map_user_proto_id_to_ndpi_id(
4657
0
      iface[0]->get_ndpi_struct(), proto.proto.master_protocol);
4658
0
  u_int16_t app_proto = ndpi_map_user_proto_id_to_ndpi_id(
4659
0
      iface[0]->get_ndpi_struct(), proto.proto.app_protocol);
4660
4661
#ifdef HAVE_NEDGE
4662
  /* On nEdge the concept of device protocol policies is only applied to
4663
   * unassigned devices on LAN */
4664
  if (pool_id != NO_HOST_POOL_ID) return device_proto_allowed;
4665
#endif
4666
4667
  /* Always allow network critical protocols */
4668
0
  if (Utils::isCriticalNetworkProtocol(master_proto) ||
4669
0
      Utils::isCriticalNetworkProtocol(app_proto))
4670
0
    return device_proto_allowed;
4671
4672
0
  if ((master_proto != NDPI_PROTOCOL_UNKNOWN) &&
4673
0
      (!ndpi_bitmask_is_set(direction_bitmask, master_proto))) {
4674
0
    return device_proto_forbidden_master;
4675
0
  } else if ((!ndpi_bitmask_is_set(direction_bitmask, app_proto))) {
4676
    /* We consider NDPI_PROTOCOL_UNKNOWN as a protocol to be allowed */
4677
0
    return device_proto_forbidden_app;
4678
0
  }
4679
4680
0
  return device_proto_allowed;
4681
0
}
4682
4683
/* ******************************************* */
4684
4685
0
void Ntop::resetStats() {
4686
0
  char buf[32];
4687
0
  last_stats_reset = time(NULL);
4688
4689
0
  snprintf(buf, sizeof(buf), "%ld", last_stats_reset);
4690
4691
  /* Saving this is essential to reset inactive hosts across ntopng restarts */
4692
0
  getRedis()->set(LAST_RESET_TIME, buf);
4693
0
}
4694
4695
/* ******************************************* */
4696
4697
0
void Ntop::refreshCPULoad() {
4698
0
  if (Utils::getCPULoad(&cpu_stats))
4699
0
    cpu_load = cpu_stats.load;
4700
0
  else
4701
0
    cpu_load = -1;
4702
0
}
4703
4704
/* ******************************************* */
4705
4706
0
bool Ntop::getCPULoad(float* out) {
4707
0
  bool rv;
4708
4709
0
  if (cpu_load >= 0) {
4710
0
    *out = cpu_load;
4711
0
    rv = true;
4712
0
  } else
4713
0
    rv = false;
4714
4715
0
  return (rv);
4716
0
}
4717
4718
/* ******************************************* */
4719
4720
0
bool Ntop::initnDPIReload() {
4721
0
  bool rc = false;
4722
4723
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4724
0
    if (getInterface(i)) rc |= getInterface(i)->initnDPIReload();
4725
4726
0
  return (rc);
4727
0
}
4728
4729
/* ******************************************* */
4730
4731
0
bool Ntop::isnDPIReloadInProgress() {
4732
0
  bool rc = false;
4733
4734
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4735
0
    if (getInterface(i)) rc |= getInterface(i)->isnDPIReloadInProgress();
4736
4737
0
  return (rc);
4738
0
}
4739
4740
/* ******************************************* */
4741
4742
0
void Ntop::finalizenDPIReload() {
4743
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4744
0
    if (getInterface(i)) getInterface(i)->finalizenDPIReload();
4745
0
}
4746
4747
/* ******************************************* */
4748
4749
bool Ntop::nDPILoadIPCategory(char* what, ndpi_protocol_category_t cat_id,
4750
0
                              char* list_name) {
4751
0
  u_int8_t list_id;
4752
0
  char* persistent_name = getPersistentCustomListName(list_name, &list_id);
4753
0
  bool success = true;
4754
0
  u_int16_t id = (((u_int16_t)list_id) << 8) + (u_int8_t)cat_id;
4755
4756
0
  for (u_int i = 0; i < get_num_interfaces(); i++) {
4757
0
    if (getInterface(i)) {
4758
0
      if (!getInterface(i)->nDPILoadIPCategory(what, id, persistent_name))
4759
0
        success = false;
4760
0
    }
4761
0
  }
4762
4763
0
  return success;
4764
0
}
4765
4766
/* ******************************************* */
4767
4768
bool Ntop::nDPILoadHostnameCategory(char* what, ndpi_protocol_category_t cat_id,
4769
0
                                    char* list_name) {
4770
0
  u_int8_t list_id;
4771
0
  char* persistent_name = getPersistentCustomListName(list_name, &list_id);
4772
0
  bool success = true;
4773
0
  u_int16_t id = (((u_int16_t)list_id) << 8) +
4774
0
                 (u_int8_t)cat_id; /* Merge list_id and cat_id on a u_int16_t */
4775
4776
0
  for (u_int i = 0; i < get_num_interfaces(); i++) {
4777
0
    if (getInterface(i)) {
4778
0
      if (!getInterface(i)->nDPILoadHostnameCategory(what, id, persistent_name))
4779
0
        success = false;
4780
0
    }
4781
0
  }
4782
4783
0
  return success;
4784
0
}
4785
4786
/* ******************************************* */
4787
4788
0
int Ntop::nDPISetDomainMask(const char* domain, u_int64_t domain_mask) {
4789
0
  int rc = 0;
4790
4791
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4792
0
    if (getInterface(i))
4793
0
      rc = getInterface(i)->setDomainMask(domain, domain_mask);
4794
4795
0
  return (rc /* last one returned */);
4796
0
}
4797
4798
/* ******************************************* */
4799
4800
0
ndpi_protocol_category_t Ntop::get_ndpi_proto_category(u_int protoid) {
4801
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4802
0
    if (getInterface(i))
4803
0
      return (getInterface(i)->get_ndpi_proto_category(protoid));
4804
4805
0
  return (NDPI_PROTOCOL_CATEGORY_UNSPECIFIED);
4806
0
}
4807
4808
/* ******************************************* */
4809
4810
void Ntop::setnDPIProtocolCategory(u_int16_t protoId,
4811
0
                                   ndpi_protocol_category_t protoCategory) {
4812
0
  for (u_int i = 0; i < get_num_interfaces(); i++) {
4813
0
    NetworkInterface* iface = getInterface(i);
4814
4815
0
    if (iface)
4816
0
      iface->setnDPIProtocolCategory(iface->get_ndpi_struct(), protoId,
4817
0
                                     protoCategory);
4818
0
  }
4819
0
}
4820
4821
/* *************************************** */
4822
4823
0
void Ntop::setLastInterfacenDPIReload(time_t now) {
4824
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4825
0
    if (getInterface(i)) getInterface(i)->setLastInterfacenDPIReload(now);
4826
0
}
4827
4828
/* *************************************** */
4829
4830
0
bool Ntop::needsnDPICleanup() {
4831
0
  bool rc = false;
4832
4833
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4834
0
    if (getInterface(i)) rc |= getInterface(i)->needsnDPICleanup();
4835
4836
0
  return (rc);
4837
0
}
4838
4839
/* *************************************** */
4840
4841
0
u_int16_t Ntop::getnDPIProtoByName(const char* name) {
4842
0
  NetworkInterface* iface = getFirstInterface();
4843
4844
0
  if (iface) return iface->getnDPIProtoByName(name);
4845
4846
0
  return (NDPI_PROTOCOL_UNKNOWN);
4847
0
}
4848
4849
/* *************************************** */
4850
4851
0
void Ntop::setnDPICleanupNeeded(bool needed) {
4852
0
  for (u_int i = 0; i < get_num_interfaces(); i++)
4853
0
    if (getInterface(i)) getInterface(i)->setnDPICleanupNeeded(needed);
4854
0
}
4855
4856
/* *************************************** */
4857
4858
8
void Ntop::setScriptsDir() {
4859
#ifdef WIN32
4860
  snprintf(scripts_dir, sizeof(scripts_dir), "%s\\scripts", get_working_dir());
4861
#else
4862
8
  snprintf(scripts_dir, sizeof(scripts_dir), "%s/scripts", get_working_dir());
4863
8
#endif
4864
8
}
4865
4866
/* ******************************************* */
4867
4868
inline int32_t Ntop::localNetworkLookup(int family, void* addr,
4869
104k
                                        u_int8_t* network_mask_bits) {
4870
104k
  return (local_network_tree.findAddress(family, addr, network_mask_bits));
4871
104k
}
4872
4873
/* ******************************************* */
4874
4875
inline int32_t Ntop::cloudNetworkLookup(int family, void* addr,
4876
0
                                        u_int8_t* network_mask_bits) {
4877
0
  return (
4878
0
      cloud_local_network_tree.findAddress(family, addr, network_mask_bits));
4879
0
}
4880
4881
/* **************************************** */
4882
4883
0
u_int32_t Ntop::getLocalNetworkId(const char* address_str) {
4884
0
  u_int32_t i;
4885
0
  u_int32_t n = local_network_tree.getNumAddresses();
4886
4887
0
  if (n == 0) return ((u_int32_t)-1);
4888
4889
0
  for (i = 0; local_network_max_id >= 0 && i <= (u_int32_t)local_network_max_id; i++) {
4890
0
    if (local_network_names[i] &&
4891
0
        (!strcmp(address_str, local_network_names[i])))
4892
0
      return (i);
4893
0
  }
4894
4895
0
  return ((u_int32_t)-1);
4896
0
}
4897
4898
/* ******************************************* */
4899
4900
4
void Ntop::initLocalNetworkMaxIdFromRedis() {
4901
4
  char **keys, **values;
4902
4
  int n;
4903
4904
4
  if (!redis) return;
4905
4906
4
  n = redis->hashGetAll(CONST_LOCAL_NETS_ID_PREFS, &keys, &values);
4907
4
  for (int i = 0; i < n; i++) {
4908
0
    char* endptr;
4909
0
    u_int32_t id = (u_int32_t)strtoul(keys[i], &endptr, 10);
4910
    /* Numeric keys are the ID→name direction; skip name→ID entries */
4911
0
    if (*endptr == '\0') {
4912
0
      if ((int32_t)id > local_network_max_id)
4913
0
        local_network_max_id = (int32_t)id;
4914
0
    }
4915
0
    free(keys[i]);
4916
0
    free(values[i]);
4917
0
  }
4918
4
  if (n > 0) {
4919
0
    free(keys);
4920
0
    free(values);
4921
0
  }
4922
4
}
4923
4924
/* ******************************************* */
4925
4926
8
bool Ntop::addLocalNetwork(char* _net) {
4927
8
  if (strncmp(_net, "asn", 3) == 0) {
4928
    /* ASN */
4929
0
    u_int32_t asn = (u_int32_t)atoi(&_net[3]);
4930
4931
0
    if (local_asn.find(asn) == local_asn.end()) {
4932
0
      local_asn[asn] = true;
4933
0
      ntop->getTrace()->traceEvent(TRACE_INFO, "Added Autonomous System %u",
4934
0
                                   asn);
4935
0
      return (true);
4936
0
    } else
4937
0
      return (false); /* Already present */
4938
8
  } else {
4939
    /* Network */
4940
8
    char *net, *position_ptr;
4941
8
    char alias[64] = "";
4942
8
    u_int32_t i, pos = 0;
4943
8
    char out[128] = {'\0'};
4944
8
    u_int32_t cur_count = local_network_tree.getNumAddresses();
4945
4946
8
    if (cur_count >= getMaxNumLocalNetworks()) {
4947
0
      ntop->getTrace()->traceEvent(
4948
0
          TRACE_ERROR, "Too many networks defined (%d): ignored %s", cur_count,
4949
0
          _net);
4950
0
      return (false);
4951
0
    }
4952
4953
    // Getting the pointer and the position to the "=" indicator
4954
8
    position_ptr = strstr(_net, "=");
4955
8
    pos = (position_ptr == NULL ? 0 : position_ptr - _net);
4956
4957
8
    if (pos) {
4958
      // "=" indicator is present inside the string
4959
      // Separating the alias from the network
4960
0
      net = strndup(_net, pos);
4961
0
      memcpy(alias, position_ptr + 1, strlen(_net) - pos - 1);
4962
8
    } else {
4963
8
      net = strdup(_net);
4964
8
    }
4965
4966
8
    if (net == NULL) {
4967
0
      ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
4968
0
      return (false);
4969
0
    }
4970
4971
    /* Check for duplicates across all active slots */
4972
8
    u_int32_t scan_limit = (u_int32_t)(local_network_max_id + 1); /* 0 when local_network_max_id == -1 */
4973
12
    for (i = 0; i < scan_limit; i++) {
4974
4
      if (local_network_names[i] && strcmp(local_network_names[i], net) == 0) {
4975
        /* Already present */
4976
0
        ntop->getTrace()->traceEvent(TRACE_NORMAL, "Network %s already present",
4977
0
                                     net);
4978
0
        free(net);
4979
0
        return (false);
4980
0
      }
4981
4
    }
4982
4983
    /*
4984
     * Get a persistent ID for this network from Redis (similar to ifname2id).
4985
     * First time the next sequential id is used. On subsequent runs the stored ID is returned,
4986
     * regardless of the order of the -m options.
4987
     */
4988
8
    u_int32_t id;
4989
8
    char rsp[16], netidx[16];
4990
8
    if (ntop->getRedis() && ntop->getRedis()->hashGet((char*)CONST_LOCAL_NETS_ID_PREFS, net, rsp, sizeof(rsp)) == 0) {
4991
0
      id = (u_int32_t)atoi(rsp);
4992
8
    } else {
4993
8
      u_int32_t fresh_id = (u_int32_t)(getMaxLocalNetworksID() + 1);
4994
8
      id = fresh_id;
4995
8
      if (ntop->getRedis()) {
4996
8
        snprintf(netidx, sizeof(netidx), "%u", id);
4997
8
        ntop->getRedis()->hashSet((char*)CONST_LOCAL_NETS_ID_PREFS, net, netidx);
4998
8
        ntop->getRedis()->hashSet((char*)CONST_LOCAL_NETS_ID_PREFS, netidx, net);
4999
8
      }
5000
8
    }
5001
5002
    // Adding the Network to the local Networks with the persistent ID
5003
8
    if (!local_network_tree.addAddresses(net, (int64_t)id)) {
5004
0
      ntop->getTrace()->traceEvent(TRACE_WARNING, "Failure adding address %s", net);
5005
0
      free(net);
5006
0
      return (false);
5007
0
    }
5008
5009
8
    local_network_names[id] = net;
5010
8
    if (local_network_max_id < 0 || (int32_t)id > local_network_max_id)
5011
8
      local_network_max_id = (int32_t)id;
5012
5013
    // Adding, if available, the alias
5014
8
    out[0] = '\0';
5015
8
    if (pos) {
5016
0
      u_int len = ndpi_min(strlen(alias), sizeof(out) - 2);
5017
5018
0
      for (u_int i = 0, j = 0; i < len; i++) {
5019
0
        if (isprint(alias[i])) out[j++] = alias[i];
5020
0
      }
5021
5022
0
      local_network_aliases[id] = strdup(out);
5023
0
    }
5024
5025
8
    ntop->getTrace()->traceEvent(TRACE_INFO, "Added Local Network #%u %s %s",
5026
8
                                 id, net, out);
5027
5028
8
    return (true);
5029
8
  }
5030
8
}
5031
5032
/* ******************************************* */
5033
5034
0
bool Ntop::getLocalNetworkAlias(lua_State* vm, u_int32_t network_id) {
5035
0
  char* alias = NULL;
5036
5037
0
  if (network_id < getMaxNumLocalNetworks())
5038
0
    alias = local_network_aliases[network_id];
5039
5040
  // Checking if the network has an alias
5041
0
  if (!alias) return false;
5042
5043
0
  lua_pushstring(vm, alias);
5044
5045
0
  return true;
5046
0
}
5047
5048
/* ******************************************* */
5049
5050
/* Format: 131.114.21.0/24,10.0.0.0/255.0.0.0 */
5051
4
void Ntop::addLocalNetworkList(const char* rule) {
5052
4
  char *tmp, *net = strtok_r((char*)rule, ",", &tmp);
5053
5054
12
  while (net != NULL) {
5055
8
    if (!addLocalNetwork(net))
5056
0
      ntop->getTrace()->traceEvent(
5057
0
          TRACE_INFO,
5058
0
          "Unable to parse network %s or already defined: skipping it", net);
5059
8
    else
5060
8
      ntop->getTrace()->traceEvent(TRACE_INFO, "Added network %s", net);
5061
5062
8
    net = strtok_r(NULL, ",", &tmp);
5063
8
  }
5064
4
}
5065
5066
/* ******************************************* */
5067
5068
0
bool Ntop::luaFlowCheckInfo(lua_State* vm, std::string check_name) const {
5069
0
  FlowChecksLoader* fcl = flow_checks_loader;
5070
0
  if (fcl) return fcl->luaCheckInfo(vm, check_name);
5071
5072
0
  return false;
5073
0
}
5074
5075
/* ******************************************* */
5076
5077
0
bool Ntop::luaHostCheckInfo(lua_State* vm, std::string check_name) const {
5078
0
  HostChecksLoader* hcl = host_checks_loader;
5079
0
  if (hcl) return hcl->luaCheckInfo(vm, check_name);
5080
5081
0
  return false;
5082
0
}
5083
5084
/* ******************************************* */
5085
5086
0
bool Ntop::isDbCreated() {
5087
0
  for (int i = 0; i < MAX_NUM_INTERFACE_IDS; i++) {
5088
0
    NetworkInterface* iface = ntop->getInterface(i);
5089
5090
0
    if (iface && (!iface->isDbCreated())) return (false);
5091
0
  }
5092
5093
0
  return (true);
5094
0
}
5095
5096
/* ******************************************* */
5097
5098
#ifndef HAVE_NEDGE
5099
5100
0
bool Ntop::initPublisher() {
5101
0
#ifdef HAVE_ZMQ
5102
0
  if (zmqPublisher == NULL) {
5103
0
    if (prefs->getZMQPublishEventsURL() == NULL) return (false);
5104
5105
0
    try {
5106
0
      zmqPublisher = new ZMQPublisher(prefs->getZMQPublishEventsURL());
5107
0
    } catch (...) {
5108
0
      zmqPublisher = NULL;
5109
0
    }
5110
0
  }
5111
5112
0
  if (zmqPublisher == NULL)
5113
0
    ntop->getTrace()->traceEvent(TRACE_WARNING,
5114
0
                                 "Unable to create ZMQ publisher");
5115
5116
0
  return !!zmqPublisher;
5117
#else
5118
  return (false);
5119
#endif
5120
0
}
5121
5122
/* ******************************************* */
5123
5124
0
bool Ntop::broadcastIPSMessage(char* msg) {
5125
0
  bool rc = false;
5126
5127
0
  if (!msg) return (false);
5128
5129
0
#ifdef HAVE_ZMQ
5130
  /* Jeopardized users_m lock :-) */
5131
0
  users_m.lock(__FILE__, __LINE__);
5132
5133
0
  if (!initPublisher()) {
5134
0
    users_m.unlock(__FILE__, __LINE__);
5135
0
    return (false);
5136
0
  }
5137
5138
0
  rc = zmqPublisher->sendIPSMessage(msg);
5139
5140
0
  users_m.unlock(__FILE__, __LINE__);
5141
0
#endif
5142
5143
0
  return (rc);
5144
0
}
5145
5146
/* ******************************************* */
5147
5148
0
bool Ntop::broadcastControlMessage(char* msg) {
5149
0
  bool rc = false;
5150
5151
0
  if (!msg) return (false);
5152
5153
0
#ifdef HAVE_ZMQ
5154
  /* Jeopardized users_m lock :-) */
5155
0
  users_m.lock(__FILE__, __LINE__);
5156
5157
0
  if (!initPublisher()) {
5158
0
    users_m.unlock(__FILE__, __LINE__);
5159
0
    return (false);
5160
0
  }
5161
5162
0
  rc = zmqPublisher->sendControlMessage(msg);
5163
5164
0
  users_m.unlock(__FILE__, __LINE__);
5165
0
#endif
5166
5167
0
  return (rc);
5168
0
}
5169
5170
#endif
5171
5172
/* ******************************************* */
5173
5174
0
u_int64_t Ntop::getNumActiveProbes() const {
5175
0
  u_int64_t n = 0;
5176
5177
0
  for (int i = 0; i < num_defined_interfaces; i++)
5178
0
    n += iface[i]->getNumActiveProbes();
5179
5180
0
  return n;
5181
0
}
5182
5183
/* ******************************************* */
5184
5185
#ifndef WIN32
5186
5187
/* ******************************************* */
5188
5189
0
Ping* Ntop::getPing(char* ifname) {
5190
0
  if (!can_send_icmp) return (NULL);
5191
5192
0
  if ((ifname == NULL) || (ifname[0] == '\0'))
5193
0
    return (default_ping);
5194
0
  else {
5195
0
    std::map<std::string /* ifname */, Ping*>::iterator it =
5196
0
        ping.find(std::string(ifname));
5197
0
    if (it == ping.end()) {
5198
      /* Pinger not found */
5199
0
      struct sockaddr_in6 sin6;
5200
0
      Ping* pinger = NULL;
5201
0
      if (Utils::readIPv4(ifname) || Utils::readIPv6(ifname, &sin6.sin6_addr)) {
5202
        /* Create pinger for the interface */
5203
0
        try {
5204
0
          pinger = new Ping(ifname);
5205
0
        } catch (...) {
5206
0
          ntop->getTrace()->traceEvent(
5207
0
              TRACE_ERROR, "Unable to create continuous pinger for %s", ifname);
5208
0
          pinger = NULL;
5209
0
        }
5210
5211
0
        if (pinger) ping[std::string(ifname)] = pinger;
5212
0
      }
5213
0
      return (pinger);
5214
0
    } else
5215
0
      return (it->second);
5216
0
  }
5217
0
}
5218
5219
/* ******************************************* */
5220
5221
0
void Ntop::initPing() {
5222
0
  if (!can_send_icmp) return;
5223
5224
0
  if (ntop->getPrefs()->get_active_monitoring_pref()) {
5225
5226
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "Initializing Continuous Ping");
5227
5228
0
    cping = new (std::nothrow) ContinuousPing();
5229
5230
    /* Default */
5231
0
    default_ping = new (std::nothrow) Ping(NULL /* System interface */);
5232
5233
    /* Pinger per interface */
5234
0
    ntop_if_t *devpointer, *cur;
5235
0
    if (Utils::ntop_findalldevs(&devpointer) == 0) {
5236
0
      for (cur = devpointer; cur; cur = cur->next) {
5237
0
        if (cur->name) {
5238
0
          struct sockaddr_in6 sin6;
5239
0
          char* real_name = Utils::get_real_name(cur->name, devpointer);
5240
0
          if (real_name == NULL /* real interface (not an alias) */ ||
5241
              /* Check if there is an IP for the interface */
5242
0
              Utils::readIPv4(cur->name) ||
5243
0
              Utils::readIPv6(cur->name, &sin6.sin6_addr)) {
5244
0
            getPing(cur->name);
5245
0
          }
5246
5247
0
          if (real_name) free(real_name);
5248
0
        }
5249
0
      }
5250
0
      Utils::ntop_freealldevs(devpointer);
5251
0
    }
5252
5253
0
    ping_initialized = true;
5254
0
  }
5255
5256
0
  for (int i = 0; i < num_defined_interfaces; i++) {
5257
0
    switch (iface[i]->getIfType()) {
5258
0
      case interface_type_PF_RING:
5259
0
      case interface_type_PCAP: {
5260
0
        char* name = iface[i]->get_name();
5261
0
        Ping* p = new (std::nothrow) Ping(name);
5262
5263
0
        if (p) {
5264
0
          ping[std::string(name)] = p;
5265
0
          ntop->getTrace()->traceEvent(TRACE_NORMAL, "Created pinger for %s",
5266
0
                                       name);
5267
0
        } else
5268
0
          ntop->getTrace()->traceEvent(
5269
0
              TRACE_WARNING, "Unable to create ping for interface %s", name);
5270
0
      } break;
5271
5272
0
      default:
5273
0
        ntop->getTrace()->traceEvent(
5274
0
            TRACE_NORMAL, "Skipping pinger for %s [ifType: %u]",
5275
0
            iface[i]->get_name(), iface[i]->getIfType());
5276
        /* Nothing to do for other interface types */
5277
0
        break;
5278
0
    }
5279
0
  }
5280
0
}
5281
5282
/* ******************************************* */
5283
5284
0
void Ntop::collectResponses(lua_State* vm) {
5285
0
  lua_newtable(vm);
5286
5287
0
  default_ping->collectResponses(vm, false /* IPv4 */);
5288
0
  default_ping->collectResponses(vm, true /* IPv6 */);
5289
5290
0
  for (std::map<std::string /* ifname */, Ping*>::iterator it = ping.begin();
5291
0
       it != ping.end(); ++it) {
5292
0
    it->second->collectResponses(vm, false /* IPv4 */);
5293
0
    it->second->collectResponses(vm, true /* IPv6 */);
5294
0
  }
5295
0
}
5296
5297
/* ******************************************* */
5298
5299
0
void Ntop::collectContinuousResponses(lua_State* vm) {
5300
0
  lua_newtable(vm);
5301
5302
0
  cping->collectResponses(vm, false /* IPv4 */);
5303
0
  cping->collectResponses(vm, true /* IPv6 */);
5304
0
}
5305
5306
#endif
5307
5308
/* ******************************************* */
5309
5310
/*
5311
  This method is needed to have a string that is not deallocated
5312
  after a call, but that is persistent inside nDPI
5313
*/
5314
char* Ntop::getPersistentCustomListName(char* list_name /* in */,
5315
0
                                        u_int8_t* list_id /* out */) {
5316
0
  std::string key(list_name);
5317
0
  std::map<std::string, u_int8_t>::iterator it = cachedCustomLists.find(key);
5318
5319
0
  if (it == cachedCustomLists.end()) {
5320
    /* Not found */
5321
0
    *list_id =
5322
0
        cachedCustomLists.size() + 1; /* Avoid 0 (= not found) as identifier */
5323
0
    cachedCustomLists[key] = *list_id;
5324
0
    it = cachedCustomLists.find(key);
5325
0
  } else
5326
0
    *list_id = it->second;
5327
5328
0
  return ((char*)it->first.c_str());
5329
0
}
5330
5331
/* ******************************************* */
5332
5333
20.9k
const char* Ntop::getPersistentCustomListNameById(u_int8_t list_id) {
5334
20.9k
  std::map<std::string, u_int8_t>::iterator it;
5335
5336
20.9k
  if (list_id > 0) {
5337
0
    for (it = cachedCustomLists.begin(); it != cachedCustomLists.end(); it++) {
5338
0
      if (it->second == list_id) return (it->first.c_str());
5339
0
    }
5340
0
  }
5341
5342
20.9k
  return (NULL);
5343
20.9k
}
5344
5345
/* ******************************************* */
5346
5347
4
void Ntop::setZoneInfo() {
5348
4
#ifndef WIN32
5349
4
  char* tz = NULL;
5350
4
  u_int num_slash = 0;
5351
4
  char buf[128];
5352
5353
4
  buf[0] = '\0';
5354
4
  zoneinfo = NULL;
5355
5356
  /* Read timezone from TZ env var */
5357
4
  tz = getenv("TZ");
5358
4
  if (tz != NULL) {
5359
0
    if (strlen(tz) > 0)
5360
0
      zoneinfo = strdup(tz);
5361
0
    else
5362
0
      tz = NULL;
5363
0
  }
5364
5365
  /* Read timezone from /etc/localtime (if TZ is not set) */
5366
4
  if (tz == NULL) {
5367
    /* Check if the softlink is defined */
5368
4
    ssize_t rc = readlink("/etc/localtime", buf, sizeof(buf));
5369
5370
4
    if (rc > 0) {
5371
4
      buf[rc] = '\0';
5372
5373
4
      rc--;
5374
5375
32
      while (rc > 0) {
5376
32
        if (buf[rc] == '/') {
5377
8
          if (++num_slash == 2) break;
5378
8
        }
5379
5380
28
        rc--;
5381
28
      }
5382
5383
4
      if (num_slash == 2) {
5384
4
        rc++;
5385
4
        zoneinfo = strdup(&buf[rc]);
5386
4
      }
5387
4
    }
5388
4
  }
5389
#ifdef __FreeBSD__
5390
  /* Last resort, in FreeBSD, it is possible the tz is inside /var/db/zoneinfo
5391
   */
5392
  if (!zoneinfo) {
5393
    FILE* fd = fopen("/var/db/zoneinfo", "r");
5394
    if (fd != NULL) {
5395
      char timezone[128];
5396
5397
      if (fgets(timezone, sizeof(timezone), fd)) {
5398
        int len = strlen(timezone);
5399
5400
        if (len > 0) timezone[len - 1] = '\0';
5401
        zoneinfo = strdup(timezone);
5402
      }
5403
5404
      fclose(fd);
5405
    }
5406
  }
5407
#endif /* FreeBSD */
5408
#else
5409
  zoneinfo = getWindowsTimezone();
5410
#endif /* WIN32 */
5411
5412
4
  if (zoneinfo == NULL) {
5413
0
    ntop->getTrace()->traceEvent(TRACE_WARNING,
5414
0
                                 "Unable to find timezone: using UTC");
5415
0
    zoneinfo = strdup("UTC");
5416
4
  } else {
5417
4
    const char* const_zoneinfo = "zoneinfo/";
5418
4
    u_int len = strlen(const_zoneinfo);
5419
5420
4
    if (strncmp(zoneinfo, const_zoneinfo, len) == 0) {
5421
0
      char* tmp = zoneinfo;
5422
0
      zoneinfo = strdup(&zoneinfo[len]);
5423
0
      free(tmp);
5424
0
    }
5425
4
  }
5426
5427
4
  if (zoneinfo)
5428
4
    ntop->getTrace()->traceEvent(TRACE_INFO, "ntopng timezone set to %s",
5429
4
                                 zoneinfo);
5430
4
}
5431
5432
/* ******************************************* */
5433
5434
// #define DEBUG_SPEEDTEST
5435
#include "../third-party/speedtest.c"
5436
5437
0
void Ntop::speedtest(lua_State* vm) {
5438
0
  json_object* rc;
5439
5440
  /*
5441
     We need to make sure that only one caller
5442
     at time calls speedtest as
5443
     - the speedtest code is not reentrant
5444
     - running multiple tests concurrently reports wrong results
5445
       as clients compete for the same bandwidth
5446
  */
5447
5448
0
  speedtest_m.lock(__FILE__, __LINE__);
5449
5450
0
  rc = ::speedtest();
5451
5452
0
  if (rc) {
5453
0
    lua_pushstring(vm, json_object_to_json_string(rc));
5454
0
    json_object_put(rc); /* Free memory */
5455
0
  } else
5456
0
    lua_pushnil(vm);
5457
5458
0
  speedtest_m.unlock(__FILE__, __LINE__);
5459
0
}
5460
5461
/* ******************************************* */
5462
5463
0
bool Ntop::createRuntimeInterface(char* name, char* source, int* iface_id) {
5464
0
  bool ret = false;
5465
0
#ifndef HAVE_NEDGE
5466
0
  bool disable_dump = true;
5467
0
  NetworkInterface *new_iface, *old_iface = NULL;
5468
0
  int slot_id = -1;
5469
5470
0
  if (old_iface_to_purge != NULL) {
5471
0
    delete old_iface_to_purge;
5472
0
    old_iface_to_purge = NULL;
5473
0
  }
5474
5475
0
  if (*iface_id != -1) {
5476
0
    for (int i = 0; i < num_defined_interfaces; i++) {
5477
0
      if (iface[i]->get_id() == *iface_id) {
5478
0
        old_iface = iface[i], slot_id = i;
5479
0
        break;
5480
0
      }
5481
0
    }
5482
0
  }
5483
5484
0
  if (strncmp(source, "pcap:", 5) == 0) {
5485
0
    source = &source[5];
5486
5487
0
    ntop->fixPath(source);
5488
5489
0
    try {
5490
0
      errno = 0;
5491
0
      new_iface = new PcapInterface((const char*)source,
5492
0
                                    (u_int8_t)ntop->get_num_interfaces(),
5493
0
                                    true /* delete pcap when done */);
5494
0
    } catch (int err) {
5495
0
      getTrace()->traceEvent(TRACE_ERROR,
5496
0
                             "Unable to open interface %s with pcap [%d]: %s",
5497
0
                             source, err, strerror(err));
5498
0
      return false;
5499
0
    }
5500
5501
    /* In case the preference is enabled,
5502
     * dump the flows from the pcap into clickhouse
5503
     */
5504
0
    if (ntop->getPrefs()->dump_pcap_to_clickhouse_enabled()) {
5505
0
      disable_dump = false;
5506
0
    }
5507
0
  } else if (strncmp(source, "db:", 3) == 0) {
5508
0
    source = &source[3];
5509
5510
#if defined(NTOPNG_PRO) && defined(HAVE_CLICKHOUSE)
5511
    if (ntop->getPrefs()->do_dump_flows_on_clickhouse()) {
5512
      try {
5513
        new_iface =
5514
            new ClickHouseInterface((const char*)source, (const char*)name);
5515
      } catch (int err) {
5516
        getTrace()->traceEvent(TRACE_ERROR, "Unable to open database on '%s'",
5517
                               source);
5518
        return false;
5519
      }
5520
    } else
5521
#endif
5522
0
    {
5523
0
      getTrace()->traceEvent(TRACE_WARNING,
5524
0
                             "Unable to create runtime interface on database "
5525
0
                             "(database support not enabled)");
5526
0
      return false;
5527
0
    }
5528
5529
0
  } else {
5530
0
    getTrace()->traceEvent(TRACE_WARNING,
5531
0
                           "Unrecognized runtime interface type '%s'", source);
5532
0
    return false;
5533
0
  }
5534
5535
0
  if (old_iface == NULL) {
5536
    /* Register new interface */
5537
0
    if (!registerInterface(new_iface)) return false;
5538
0
  }
5539
5540
0
  initInterface(new_iface, disable_dump /* disable flow dump to db */);
5541
0
  new_iface->reloadFlowChecks(flow_checks_loader);
5542
0
  new_iface->reloadHostChecks(host_checks_loader);
5543
0
  new_iface->allocateStructures(true /* disable flow dump to db */);
5544
0
  new_iface->startPacketPolling();
5545
5546
0
  if (old_iface != NULL) {
5547
0
    m.lock(__FILE__, __LINE__);
5548
5549
0
    iface[slot_id] = new_iface; /* Swap interfaces */
5550
5551
0
    old_iface->shutdown();
5552
5553
    /* Wait until the interface is shutdown */
5554
0
    while (old_iface->isRunning()) sleep(1);
5555
5556
    /* Trick to avoid crashing while using the same interface we want to free */
5557
0
    old_iface_to_purge = old_iface;
5558
5559
0
    m.unlock(__FILE__, __LINE__);
5560
0
  }
5561
5562
0
  *iface_id = new_iface->get_id();
5563
5564
0
  ret = true;
5565
5566
0
#endif
5567
5568
0
  return (ret);
5569
0
}
5570
5571
/* ******************************************* */
5572
5573
0
void Ntop::incBlacklisHits(std::string listname) { blStats.incHits(listname); }
5574
5575
/* ******************************************* */
5576
5577
#ifdef NTOPNG_PRO
5578
5579
bool Ntop::incNumFlowExporters(u_int32_t unique_source_id) {
5580
  std::map<u_int32_t, u_int32_t>::iterator it;
5581
  bool success = false;
5582
5583
  flow_exporters_lock.lock(__FILE__, __LINE__);
5584
5585
  it = flow_exporters_count.find(unique_source_id);
5586
  if (it != flow_exporters_count.end()) {
5587
    it->second++; /* Already present, increase the count only */
5588
    success = true;
5589
  } else if (num_flow_exporters < get_max_num_flow_exporters()) {
5590
    flow_exporters_count[unique_source_id] = 1; /* First occurrence */
5591
    num_flow_exporters++;
5592
    success = true;
5593
  }
5594
5595
  flow_exporters_lock.unlock(__FILE__, __LINE__);
5596
5597
  return success;
5598
}
5599
5600
/* ******************************************* */
5601
5602
bool Ntop::incNumFlowExportersInterfaces(u_int32_t unique_source_id, u_int32_t ifIndex) {
5603
  std::pair<u_int32_t, u_int32_t> key(unique_source_id, ifIndex);
5604
  std::map<std::pair<u_int32_t, u_int32_t>, u_int32_t>::iterator it;
5605
  bool success = false;
5606
5607
  flow_exporters_lock.lock(__FILE__, __LINE__);
5608
5609
  it = flow_interfaces_count.find(key);
5610
  if (it != flow_interfaces_count.end()) {
5611
    it->second++; /* Already present, increase the count only */
5612
    success = true;
5613
  } else if (num_flow_interfaces < get_max_num_flow_exporters_interfaces()) {
5614
    flow_interfaces_count[key] = 1; /* First occurrence */
5615
    num_flow_interfaces++;
5616
    success = true;
5617
  }
5618
5619
  flow_exporters_lock.unlock(__FILE__, __LINE__);
5620
5621
  return success;
5622
}
5623
5624
/* ******************************************* */
5625
5626
void Ntop::decNumFlowExporters(u_int32_t unique_source_id) {
5627
  std::map<u_int32_t, u_int32_t>::iterator it;
5628
5629
  flow_exporters_lock.lock(__FILE__, __LINE__);
5630
5631
  it = flow_exporters_count.find(unique_source_id);
5632
  if (it != flow_exporters_count.end()) {
5633
    it->second--;
5634
    if (it->second == 0) {
5635
      flow_exporters_count.erase(it);
5636
      if (num_flow_exporters > 0) num_flow_exporters--;
5637
    }
5638
  }
5639
5640
  flow_exporters_lock.unlock(__FILE__, __LINE__);
5641
}
5642
5643
/* ******************************************* */
5644
5645
void Ntop::decNumFlowExportersInterfaces(u_int32_t unique_source_id, u_int32_t ifIndex) {
5646
  std::pair<u_int32_t, u_int32_t> key(unique_source_id, ifIndex);
5647
  std::map<std::pair<u_int32_t, u_int32_t>, u_int32_t>::iterator it;
5648
5649
  flow_exporters_lock.lock(__FILE__, __LINE__);
5650
5651
  it = flow_interfaces_count.find(key);
5652
  if (it != flow_interfaces_count.end()) {
5653
    it->second--;
5654
    if (it->second == 0) {
5655
      flow_interfaces_count.erase(it);
5656
      if (num_flow_interfaces > 0) num_flow_interfaces--;
5657
    }
5658
  }
5659
5660
  flow_exporters_lock.unlock(__FILE__, __LINE__);
5661
}
5662
5663
/* ******************************************* */
5664
5665
u_int32_t Ntop::getMaxNumFlowExporters() {
5666
  return get_max_num_flow_exporters();
5667
}
5668
5669
/* ******************************************* */
5670
5671
u_int32_t Ntop::getMaxNumFlowExportersInterfaces() {
5672
  return get_max_num_flow_exporters_interfaces();
5673
}
5674
#endif /* NTOPNG_PRO */
5675
5676
/* ******************************************* */
5677
5678
8
u_int32_t Ntop::getMaxNumLocalNetworks() {
5679
#ifdef NTOPNG_PRO
5680
  return get_max_num_local_networks();
5681
#else
5682
8
  return MAX_NUM_LOCAL_NETWORKS;
5683
8
#endif /* NTOPNG_PRO */
5684
8
}
5685
5686
/* ******************************************* */
5687
5688
25.8k
bool Ntop::isInLocalASN(IpAddress* ip) {
5689
25.8k
  u_int32_t asn;
5690
5691
25.8k
  if ((geo == NULL) || (local_asn.size() == 0 /* No ASN defined */))
5692
25.8k
    return (false);
5693
5694
0
  if (geo->getAS(ip, &asn, NULL)) {
5695
0
    if (local_asn.find(asn) != local_asn.end()) return (true);
5696
0
  }
5697
5698
0
  return (false);
5699
0
}
5700
5701
/* ******************************************* */
5702
5703
0
#define TMP_PROTOS_FILE "/tmp/ntopng-ndpi-protocols-file"
5704
5705
0
bool Ntop::downloadCustomnDPIProtos(char* url, char* dest_file) {
5706
0
  struct stat statbuf;
5707
5708
0
  if (stat(dest_file, &statbuf) == 0) {
5709
    /* File exists */
5710
0
    time_t tdiff = time(NULL) - statbuf.st_mtime;
5711
5712
    // ntop->getTrace()->traceEvent(TRACE_WARNING, "-> %u", tdiff);
5713
5714
0
    if (tdiff <= 5 /* sec */)
5715
0
      return (true); /* Fresh file: no need to re-download it */
5716
0
  }
5717
5718
0
  HttpGetPostOptions opts;
5719
0
  memset(&opts, 0, sizeof(opts));
5720
0
  opts.connect_timeout      = 10;
5721
0
  opts.max_duration_timeout = 30;
5722
0
  opts.write_fname          = dest_file;
5723
0
  opts.follow_redirects     = true;
5724
0
  opts.ip_version           = 4;
5725
0
  bool rc = Utils::httpGetPostPutPatch(NULL, custom_ndpi_protos, method_get, opts);
5726
5727
0
  if (rc == false) {
5728
0
    ntop->getTrace()->traceEvent(TRACE_WARNING,
5729
0
                                 "Unable to access URL %s: ignored", url);
5730
0
    return (false);
5731
0
  } else {
5732
0
    ntop->getTrace()->traceEvent(
5733
0
        TRACE_NORMAL, "Successfully downloaded nDPI protocols file from URL %s",
5734
0
        url);
5735
0
    return (true);
5736
0
  }
5737
0
}
5738
5739
/* ******************************************* */
5740
5741
10
char* Ntop::getCustomnDPIProtos() {
5742
10
  if (custom_ndpi_protos != NULL) {
5743
0
    if ((strncmp(custom_ndpi_protos, "http://", 7) == 0) ||
5744
0
        (strncmp(custom_ndpi_protos, "https://", 8) == 0)) {
5745
0
      downloadCustomnDPIProtos(custom_ndpi_protos, (char*)TMP_PROTOS_FILE);
5746
5747
0
      return ((char*)TMP_PROTOS_FILE);
5748
0
    }
5749
0
  }
5750
5751
10
  return (custom_ndpi_protos);
5752
10
}
5753
5754
/* ******************************************* */
5755
5756
void Ntop::trackAssetChange(const char* protocol, const char* action, Mac* mac,
5757
                            IpAddress* target_ip, Host* target, Flow* flow,
5758
359
                            char* note) {
5759
#ifdef TRACK_ASSET_CHANGE
5760
  char buf[64], buf1[256], buf2[256];
5761
5762
  ntop->getTrace()->traceEvent(
5763
      TRACE_NORMAL, "[%s] %s [%s][%s][%s][%s]", protocol, action,
5764
      mac ? mac->print(buf, sizeof(buf)) : "",
5765
      target_ip ? target_ip->print(buf1, sizeof(buf1)) : "",
5766
      target ? target->print(buf1, sizeof(buf1)) : "",
5767
      flow ? flow->print(buf2, sizeof(buf2), false) : "", note ? note : "");
5768
#endif
5769
359
}
5770
5771
/* ******************************************* */
5772
5773
0
void Ntop::reloadASNConfiguration() {
5774
0
  getPrefs()->reloadASNConfiguration();
5775
0
  for (int i = 0; i < MAX_NUM_INTERFACE_IDS; i++) {
5776
0
    NetworkInterface* iface = ntop->getInterface(i);
5777
0
    if (iface && iface->isZMQInterface()) {
5778
0
      ntop->getTrace()->traceEvent(TRACE_NORMAL,
5779
0
                                   "Updating ASN Exporter Prefs [ifid: %d]",
5780
0
                                   iface->get_id());
5781
0
      iface->updateASNExportersPrefs();
5782
0
    }
5783
0
  }
5784
0
}
5785
5786
/* ******************************************* */
5787
5788
0
bool Ntop::viewHasZMQInterface(NetworkInterface* viewInterface) {
5789
#ifdef NTOPNG_PRO
5790
  for (int i = 0; i < num_defined_interfaces; i++) {
5791
    if (iface[i] && (iface[i]->viewedBy() == viewInterface) &&
5792
        (iface[i]->getIfType() == interface_type_ZMQ))
5793
      return (true);
5794
  }
5795
#endif
5796
5797
0
  return (false);
5798
0
}
5799
5800
/* ******************************************* */
5801
5802
0
std::string Ntop::getLuaCache(std::string key) {
5803
0
  std::map<std::string, std::string>::iterator it;
5804
5805
0
  luaCacheLock.rdlock(__FILE__, __LINE__);
5806
0
  it = luaCache.find(key);
5807
0
  luaCacheLock.unlock(__FILE__, __LINE__);
5808
5809
0
  if (it == luaCache.end())
5810
0
    return ("");
5811
0
  else
5812
0
    return (it->second);
5813
0
}
5814
5815
/* ******************************************* */
5816
5817
0
void Ntop::setLuaCache(std::string key, std::string val) {
5818
0
  luaCacheLock.wrlock(__FILE__, __LINE__);
5819
0
  luaCache[key] = val;
5820
0
  luaCacheLock.unlock(__FILE__, __LINE__);
5821
0
}
5822
5823
/* ******************************************* */
5824
5825
0
void Ntop::dumpLuaCache(lua_State* vm) {
5826
0
  std::map<std::string, std::string>::iterator it;
5827
5828
0
  lua_newtable(vm);
5829
5830
0
  luaCacheLock.rdlock(__FILE__, __LINE__);
5831
5832
0
  for (it = luaCache.begin(); it != luaCache.end(); ++it)
5833
0
    lua_push_str_table_entry(vm, it->first.c_str(), it->second.c_str());
5834
5835
0
  luaCacheLock.unlock(__FILE__, __LINE__);
5836
0
}
5837
5838
/* ******************************************* */
5839
5840
#ifdef NTOPNG_PRO
5841
5842
void Ntop::initSnmpInterfaceRole() {
5843
  if(ifRoles_shadow != NULL) {
5844
    delete ifRoles_shadow;
5845
    ifRoles_shadow = NULL;
5846
  }
5847
  
5848
  ifRoles_shadow = new (std::nothrow) std::map<std::tuple<struct ndpi_in6_addr, u_int32_t>, SNMPInterfaceRole, InterfaceKeyCompare>;
5849
}
5850
5851
/* ******************************************* */
5852
5853
void Ntop::activateSnmpInterfaceRoles() {
5854
  if(ifRoles_shadow != NULL) {
5855
    std::map<std::tuple<struct ndpi_in6_addr, u_int32_t>, SNMPInterfaceRole, InterfaceKeyCompare> *tmp = ifRoles;
5856
5857
    ifRoles = ifRoles_shadow;
5858
    ifRoles_shadow = tmp;
5859
  } else
5860
    ntop->getTrace()->traceEvent(TRACE_ERROR, "Invalid state");
5861
}
5862
5863
/* ******************************************* */
5864
5865
void Ntop::snmpSetInterfaceRole(struct ndpi_in6_addr *exporter_device_ip,
5866
                                u_int32_t interface_id,
5867
                                SNMPInterfaceRole interface_role) {
5868
  if(ifRoles_shadow == NULL) initSnmpInterfaceRole();
5869
5870
  if(ifRoles_shadow)
5871
    ifRoles_shadow->emplace(std::make_tuple(*exporter_device_ip, interface_id), interface_role);
5872
}
5873
5874
/* ******************************************* */
5875
5876
/* NOTE: add a mutex or another trick if dynamic interface reload is implemented */
5877
SNMPInterfaceRole Ntop::snmpGetInterfaceRole(struct ndpi_in6_addr *exporter_device_ip,
5878
                                             u_int32_t interface_id) {
5879
  if(ifRoles != NULL) {
5880
    std::tuple<struct ndpi_in6_addr, u_int32_t> searchKey = {*exporter_device_ip, interface_id};
5881
    std::map<std::tuple<struct ndpi_in6_addr, u_int32_t>, SNMPInterfaceRole, InterfaceKeyCompare>::iterator it =
5882
      ifRoles->find(searchKey);
5883
5884
    if (it != ifRoles->end())
5885
      return (it->second);    
5886
  }
5887
  
5888
  return (role_other);
5889
}
5890
#endif
5891
5892
/* ******************************************* */
5893
5894
0
struct ndpi_in6_addr Ntop::findExporterIPMgmtAddress(struct ndpi_in6_addr host_ip) {
5895
0
  struct ndpi_in6_addr empty;  
5896
#ifdef NTOPNG_PRO
5897
  std::unordered_map<struct ndpi_in6_addr, struct ndpi_in6_addr,
5898
         ndpi_in6_addr_hash, ndpi_in6_addr_eq>::iterator it;
5899
5900
  it = snmp_ip_addr_map.find(host_ip);
5901
  
5902
  if(it != snmp_ip_addr_map.end())
5903
    return(it->second);
5904
#endif
5905
5906
0
  memset(&empty, 0, sizeof(empty));
5907
0
  return(empty);
5908
0
}
5909