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 | | |