191 if (commands.empty()) {
192 if (out_log) *out_log =
"No commands";
196 char tmpl[] =
"/tmp/BastionGuard_all_XXXXXX";
197 int fd = mkstemp(tmpl);
199 if (out_log) *out_log =
_(
"Impossibile creare file temporaneo");
202 std::string tmpname = tmpl;
204 FILE* f = fdopen(fd,
"w");
207 if (out_log) *out_log =
_(
"fdopen fallita");
211 fputs(
"#!/bin/bash\nset -euo pipefail\n\n", f);
212 fputs(
"echo \"[BastionGuard] start elevated script\" >&2\n", f);
213 for (
const auto &c : commands) {
218 fputs(
"echo \"[BastionGuard] end elevated script\" >&2\n", f);
220 chmod(tmpname.c_str(), 0750);
222 bool use_pkexec = fs::exists(
"/usr/bin/pkexec");
223 bool has_action_id = run_cmd_capture(
"pkexec --help | grep -q action-id");
224 std::ostringstream elev;
227 elev <<
"pkexec --disable-internal-agent --action-id org.BastionGuard.maintenance " << tmpname;
229 elev <<
"pkexec " << tmpname;
232 elev <<
"sudo " << tmpname;
237 bool ok = run_cmd_capture(elev.str(), &out, &rc);
241 fs::remove(tmpname, ec);
243 if (out_log) *out_log = out;
244 return ok && rc == 0;
290 bool group_modified =
false;
293 std::cout <<
"[wizard] " <<
_(
"Apro le porte firewall necessarie (53/udp, 81/tcp, 444/tcp, 4443/tcp)...") << std::endl;
295 auto detect_distro_family = []() -> std::string {
296 std::ifstream f(
"/etc/os-release");
297 if (!f.is_open())
return "unknown";
298 std::string line, content;
299 while (std::getline(f, line)) content += line +
"\n";
302 if (content.find(
"ID=fedora") != std::string::npos ||
303 content.find(
"ID_LIKE=fedora") != std::string::npos ||
304 content.find(
"ID_LIKE=rhel") != std::string::npos)
306 if (content.find(
"ID=arch") != std::string::npos ||
307 content.find(
"ID_LIKE=arch") != std::string::npos)
309 if (content.find(
"ID=opensuse") != std::string::npos)
311 if (content.find(
"ID=debian") != std::string::npos ||
312 content.find(
"ID_LIKE=debian") != std::string::npos ||
313 content.find(
"ID=ubuntu") != std::string::npos)
318 std::string distro = detect_distro_family();
319 std::cout <<
"[wizard] " <<
_(
"Rilevata distribuzione: ") << distro << std::endl;
321 auto try_firewalld = [&]() ->
bool {
322 if (!fs::exists(
"/usr/bin/firewall-cmd"))
return false;
323 std::string cmd = elev_cmd(
324 "firewall-cmd --permanent --add-port=53/udp && "
325 "firewall-cmd --permanent --add-port=81/tcp && "
326 "firewall-cmd --permanent --add-port=444/tcp && "
327 "firewall-cmd --permanent --add-port=4443/tcp && "
328 "firewall-cmd --reload"
330 std::string out;
int rc = 0;
331 bool ok = run_cmd_capture(cmd, &out, &rc);
333 std::cout <<
"[wizard] ✅ " <<
_(
"Porte aperte tramite firewalld.") << std::endl;
337 auto try_ufw = [&]() ->
bool {
338 if (!fs::exists(
"/usr/sbin/ufw") && !fs::exists(
"/usr/bin/ufw"))
return false;
339 std::ostringstream cmd;
340 cmd <<
"ufw allow 53/udp && ufw allow 81/tcp && ufw allow 444/tcp && ufw allow 4443/tcp";
341 std::string out;
int rc = 0;
342 bool ok = run_cmd_capture(elev_cmd(cmd.str(),
"org.BastionGuard.nftables"), &out, &rc);
345 std::cout <<
"[wizard] ✅ " <<
_(
"Porte aperte tramite ufw.") << std::endl;
349 auto try_nft = [&]() ->
bool {
350 if (!fs::exists(
"/usr/sbin/nft") && !fs::exists(
"/usr/bin/nft"))
return false;
352 run_cmd_capture(elev_cmd(
"modprobe nf_nat 2>/dev/null || true"),
nullptr,
nullptr);
353 run_cmd_capture(elev_cmd(
"sh -c 'echo nf_nat > /etc/modules-load.d/BastionGuard.conf 2>/dev/null || true'"),
nullptr,
nullptr);
355 std::string cmd = elev_cmd(
356 "nft add table inet BastionGuard_filter 2>/dev/null || true; "
357 "nft add chain inet BastionGuard_filter input { type filter hook input priority 0; policy accept; } 2>/dev/null || true; "
358 "nft add rule inet BastionGuard_filter input udp dport 53 accept || true; "
359 "nft add rule inet BastionGuard_filter input tcp dport 81 accept || true; "
360 "nft add rule inet BastionGuard_filter input tcp dport 444 accept || true; "
361 "nft add rule inet BastionGuard_filter input tcp dport 4443 accept || true"
363 std::string out;
int rc = 0;
364 run_cmd_capture(cmd, &out, &rc);
365 std::cout <<
"[wizard] ✅ " <<
_(
"Regole nftables applicate (best-effort).") << std::endl;
369 auto try_iptables = [&]() ->
bool {
370 if (!fs::exists(
"/sbin/iptables") && !fs::exists(
"/usr/sbin/iptables"))
return false;
371 std::string cmd = elev_cmd(
372 "iptables -I INPUT -p udp --dport 53 -j ACCEPT || true; "
373 "iptables -I INPUT -p tcp --dport 81 -j ACCEPT || true; "
374 "iptables -I INPUT -p tcp --dport 444 -j ACCEPT || true; "
375 "iptables -I INPUT -p tcp --dport 4443 -j ACCEPT || true"
377 std::string out;
int rc = 0;
378 run_cmd_capture(cmd, &out, &rc);
379 std::cout <<
"[wizard] ✅ " <<
_(
"Regole iptables applicate (best-effort).") << std::endl;
384 if (try_firewalld()) ok =
true;
385 else if (try_ufw()) ok =
true;
386 else if (try_nft()) ok =
true;
387 else if (try_iptables()) ok =
true;
389 std::cerr <<
"[wizard] ⚠ " <<
_(
"Nessuno strumento firewall rilevato; apri manualmente le porte se necessario.") << std::endl;
391 const char* user = getenv(
"SUDO_USER");
392 if (!user || !*user) {
393 const char* pk_uid = getenv(
"PKEXEC_UID");
394 if (pk_uid && *pk_uid) {
395 struct passwd* pwuid_entry = getpwuid(
static_cast<uid_t
>(std::stoi(pk_uid)));
397 user = pwuid_entry->pw_name;
401 user = getenv(
"USER");
404 std::cout <<
"[wizard] " <<
_(
"Utente effettivo: ") << user << std::endl;
407 std::istringstream words(groups.output);
410 while (words >> group) {
411 if (group ==
"adm" || group ==
"systemd-journal") { member =
true;
break; }
413 const int rc_check = groups.ok() && member ? 0 : 1;
416 std::ostringstream cmd;
418 <<
"if getent group systemd-journal >/dev/null 2>&1; then "
422 std::cout <<
"[wizard] " <<
_(
"Aggiungo l'utente ai gruppi di log disponibili...") << std::endl;
423 bool okgrp = run_cmd_capture(elev_cmd(cmd.str()), &out, &rc);
424 if (okgrp && rc == 0) {
425 std::cout <<
"[wizard] ✅ " <<
_(
"Utente aggiunto ai gruppi richiesti.") << std::endl;
426 group_modified =
true;
428 std::cerr <<
"[wizard] ⚠ " <<
_(
"Errore aggiungendo l'utente ai gruppi: ") << out << std::endl;
431 std::cout <<
"[wizard] " <<
_(
"Utente già presente in un gruppo di accesso ai log.") << std::endl;
434 std::cerr <<
"[wizard] ⚠ " <<
_(
"Impossibile determinare l'utente effettivo; salto l'aggiunta ai gruppi.") << std::endl;
437 if (group_modified) {
438 auto* dlg =
new Gtk::MessageDialog(
439 _(
"Per completare la configurazione,\ntermina e riavvia la sessione utente."),
441 Gtk::MessageType::INFO,
442 Gtk::ButtonsType::OK,
445 dlg->set_title(
_(
"BastionGuard - Permessi aggiornati"));
446 dlg->set_secondary_text(
447 _(
"I nuovi permessi di accesso ai log diventeranno attivi dopo il logout.\n"
448 "Chiudi e riapri la sessione per visualizzare correttamente l'avvio dell'applicazione BastionGuard.")
450 dlg->signal_response().connect([dlg](
int) { dlg->hide();
delete dlg; });
455 std::cout <<
"[wizard] " <<
_(
"Riavvio del servizio BastionGuard-phishing-scanner...") << std::endl;
458 std::string out;
int rc = 0;
459 run_cmd_capture(
"test -x /usr/libexec/bastionguard/bastionguard-service", &out, &rc);
461 std::cout <<
"[wizard] " <<
_(
"Gestore servizi non presente: salto il riavvio del servizio.") << std::endl;
464 run_cmd_capture(elev_cmd(
"/usr/libexec/bastionguard/bastionguard-service --system exists BastionGuard-phishing-scanner.service >/dev/null 2>&1"), &out, &rc);
466 run_cmd_capture(elev_cmd(
"/usr/libexec/bastionguard/bastionguard-service --system restart BastionGuard-phishing-scanner.service >/dev/null 2>&1 || true",
"org.BastionGuard.service.manage"), &out, &rc);
469 std::cout <<
"[wizard] ✅ " <<
_(
"Servizio BastionGuard-phishing-scanner riavviato.") << std::endl;
471 std::cerr <<
"[wizard] ⚠ " <<
_(
"Tentativo di riavvio eseguito (codice ") << rc <<
_(
"). Verifica lo stato del servizio.") << std::endl;
474 std::cout <<
"[wizard] " <<
_(
"Servizio BastionGuard-phishing-scanner non trovato: salto il riavvio.") << std::endl;
481 }
catch (
const std::exception& ex) {
482 std::cerr <<
"[wizard] ⚠ " <<
_(
"Eccezione in setup_firewall_ports: ") << ex.what() << std::endl;
551 std::cerr <<
_(
"[wizard] Installazione CA di sistema bloccata su Fedora/RHEL/Alma/Rocky.\n");
555 if (!fs::exists(ca_crt_path)) {
556 std::cerr <<
"[wizard] " <<
_(
"File CA non trovato:") <<
" " << ca_crt_path << std::endl;
560 std::ostringstream script;
561 script <<
"#!/bin/sh\nset -e\n\n";
562 script <<
"CA=\"" << ca_crt_path <<
"\"\n";
563 script <<
"OK=0\n\n";
565 script <<
"if command -v update-ca-certificates >/dev/null 2>&1; then\n";
566 script <<
" install -m 644 \"$CA\" /usr/local/share/ca-certificates/BastionGuard-ca.crt && update-ca-certificates && OK=1 || true\n";
570 script <<
"if command -v trust >/dev/null 2>&1; then\n";
571 script <<
" trust anchor --store \"$CA\" && OK=1 || true\n";
574 script <<
"if command -v update-ca-trust >/dev/null 2>&1; then\n";
575 script <<
" install -m 644 \"$CA\" /etc/pki/ca-trust/source/anchors/BastionGuard-ca.crt && update-ca-trust extract && OK=1 || true\n";
578 script <<
"if [ -d /etc/ca-certificates/trust-source/anchors ]; then\n";
579 script <<
" install -m 644 \"$CA\" /etc/ca-certificates/trust-source/anchors/BastionGuard-ca.crt && \\\n";
580 script <<
" ( (command -v trust >/dev/null 2>&1 && trust extract-compat) || (trust anchor --store \"$CA\") ) && OK=1 || true\n";
582 script <<
"install -m 644 \"$CA\" /usr/local/share/ca-certificates/BastionGuard-ca.crt || true\n";
583 script <<
"install -m 644 \"$CA\" /etc/pki/ca-trust/source/anchors/BastionGuard-ca.crt || true\n";
584 script <<
"if command -v update-ca-certificates >/dev/null 2>&1; then update-ca-certificates || true; fi\n";
585 script <<
"if command -v update-ca-trust >/dev/null 2>&1; then update-ca-trust extract || true; fi\n";
586 script <<
"if command -v trust >/dev/null 2>&1; then trust anchor --store \"$CA\" || true; fi\n\n";
588 script <<
"if [ \"$OK\" -eq 1 ]; then\n";
589 script <<
" echo \"OK: CA installed into system trust\"\n";
590 script <<
" exit 0\n";
592 script <<
" echo \"WARN: none of the system trust installers succeeded; CA copied to common paths\"\n";
593 script <<
" exit 2\n";
597 bool ok = write_temp_script_and_elevate(script.str(), &out);
600 std::cerr <<
"[wizard] " <<
_(
"Installazione CA: script restituisce warning o errore") <<
"\n" << out << std::endl;
601 if (out.find(
"OK: CA installed") != std::string::npos) {
602 std::cout <<
"[wizard] " <<
_(
"CA installata nel trust store di sistema") << std::endl;
605 if (out.find(
"WARN:") != std::string::npos) {
606 std::cout <<
"[wizard] " <<
_(
"CA copiata in directory comuni, ma potresti doverla abilitare manualmente") << std::endl;
612 std::cout <<
"[wizard] " <<
_(
"CA installata nel trust store di sistema") << std::endl;
615 }
catch (
const std::exception& ex) {
616 std::cerr <<
"[wizard] " <<
_(
"Eccezione in install_system_ca:") <<
" " << ex.what() << std::endl;
693 namespace fs = std::filesystem;
695 std::cout <<
"[wizard] " <<
_(
"Installazione native messaging host...") << std::endl;
696 std::string bin_path =
"/usr/bin/BastionGuard-native-host";
698 if (fs::exists(bin_path)) {
700 auto perms = fs::status(bin_path, ec).permissions();
701 if (!ec && ((perms & fs::perms::owner_exec) == fs::perms::none)) {
704 fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec,
705 fs::perm_options::add,
709 std::cerr <<
"[wizard] " <<
_(
"Impossibile impostare i permessi eseguibili: ")
710 << ec.message() << std::endl;
714 std::cerr <<
"[wizard] " <<
_(
"Avviso: binario native host non trovato: ") << bin_path << std::endl;
717 auto json_escape = [](
const std::string& s) {
720 if (c ==
'\\') out +=
"\\\\";
721 else if (c ==
'"') out +=
"\\\"";
727 auto make_chrome_manifest = [&](
const std::string& ext_id) {
728 std::ostringstream o;
730 <<
" \"name\": \"com.BastionGuard.native_host\",\n"
731 <<
" \"description\": \"BastionGuard native messaging host\",\n"
732 <<
" \"path\": \"" << json_escape(bin_path) <<
"\",\n"
733 <<
" \"type\": \"stdio\",\n"
734 <<
" \"allowed_origins\": [\n";
736 o <<
" \"chrome-extension://" << ext_id <<
"/\"\n";
742 auto make_firefox_manifest = [&](
const std::string& ext_id) {
743 std::ostringstream o;
745 <<
" \"name\": \"com.BastionGuard.native_host\",\n"
746 <<
" \"description\": \"BastionGuard native messaging host\",\n"
747 <<
" \"path\": \"" << json_escape(bin_path) <<
"\",\n"
748 <<
" \"type\": \"stdio\",\n"
749 <<
" \"allowed_extensions\": [\n";
751 o <<
" \"" << ext_id <<
"\"\n";
757 const char* xdg = getenv(
"XDG_CONFIG_HOME");
758 std::string config_base = (xdg && *xdg)
760 : (std::string(getenv(
"HOME")) +
"/.config");
762 std::vector<std::pair<std::string,std::string>> manifest_targets;
764 manifest_targets.emplace_back(config_base +
"/google-chrome/NativeMessagingHosts", make_chrome_manifest(extension_id));
765 manifest_targets.emplace_back(config_base +
"/chromium/NativeMessagingHosts", make_chrome_manifest(extension_id));
766 manifest_targets.emplace_back(config_base +
"/BraveSoftware/Brave-Browser/NativeMessagingHosts", make_chrome_manifest(extension_id));
767 manifest_targets.emplace_back(config_base +
"/microsoft-edge/NativeMessagingHosts", make_chrome_manifest(extension_id));
769 manifest_targets.emplace_back(std::string(getenv(
"HOME")) +
"/.mozilla/native-messaging-hosts", make_firefox_manifest(
"BastionGuard@local"));
770 manifest_targets.emplace_back(std::string(getenv(
"HOME")) +
"/.librewolf/native-messaging-hosts", make_firefox_manifest(
"BastionGuard@local"));
772 manifest_targets.emplace_back(
"/etc/opt/chrome/native-messaging-hosts", make_chrome_manifest(extension_id));
773 manifest_targets.emplace_back(
"/etc/chromium/native-messaging-hosts", make_chrome_manifest(extension_id));
774 manifest_targets.emplace_back(
"/usr/lib/mozilla/native-messaging-hosts", make_firefox_manifest(
"BastionGuard@local"));
776 bool any_written =
false;
777 for (
auto& t : manifest_targets) {
778 const std::string& dir = t.first;
779 const std::string& content = t.second;
781 fs::create_directories(dir, ec);
783 std::cerr <<
"[wizard] " <<
_(
"Impossibile creare la directory: ")
784 << dir <<
" (" << ec.message() <<
")" << std::endl;
787 std::string manifest_name = dir +
"/com.BastionGuard.native_host.json";
789 if (fs::exists(manifest_name)) {
790 std::cout <<
"[wizard] " <<
_(
"Manifest già presente, salto: ") << manifest_name << std::endl;
794 std::ofstream man(manifest_name, std::ios::trunc);
798 std::cout <<
"[wizard] " <<
_(
"Manifest scritto in: ") << manifest_name << std::endl;
801 std::cerr <<
"[wizard] " <<
_(
"Impossibile scrivere manifest in: ") << manifest_name << std::endl;
806 std::cout <<
"[wizard] " <<
_(
"Native host configurato, manifest creati") << std::endl;
810 std::cerr <<
"[wizard] " <<
_(
"Nessun manifest creato (tutti già presenti?)") << std::endl;
813 }
catch (
const std::exception& ex) {
814 std::cerr <<
"[wizard] " <<
_(
"Eccezione in install_native_host: ") << ex.what() << std::endl;
821 bool run_all = options.empty();
824 auto has_opt = [&](
const std::string& key) {
825 return run_all || std::find(options.begin(), options.end(), key) != options.end();
828 std::vector<std::string> cmds;
830 std::cout <<
_(
"[wizard] 🔁 Esecuzione privilegiata combinata (una sola autenticazione)...\n");
836 cmds.push_back(
_(
"echo '[BastionGuard] Genero certificati locali...' >&2"));
837 cmds.push_back(
"mkdir -p /etc/BastionGuard/certs && chmod 755 /etc/BastionGuard/certs");
838 cmds.push_back(
"[ -f /etc/BastionGuard/certs/BastionGuard-ca.key.pem ] || openssl genrsa -out /etc/BastionGuard/certs/BastionGuard-ca.key.pem 4096");
839 cmds.push_back(
"[ -f /etc/BastionGuard/certs/BastionGuard-ca.crt.pem ] || openssl req -x509 -new -nodes "
840 "-key /etc/BastionGuard/certs/BastionGuard-ca.key.pem -sha256 -days 3650 "
841 "-subj '/CN=BastionGuard Local CA' -out /etc/BastionGuard/certs/BastionGuard-ca.crt.pem");
842 cmds.push_back(
"[ -f /etc/BastionGuard/certs/local-warning.key.pem ] || openssl genrsa -out /etc/BastionGuard/certs/local-warning.key.pem 2048");
843 cmds.push_back(
"openssl req -new -key /etc/BastionGuard/certs/local-warning.key.pem "
844 "-subj '/CN=localhost' -out /etc/BastionGuard/certs/local-warning.csr.pem");
845 cmds.push_back(
"echo 'subjectAltName = DNS:localhost,IP:127.0.0.2' > /etc/BastionGuard/certs/v3ext.cnf");
846 cmds.push_back(
"openssl x509 -req -in /etc/BastionGuard/certs/local-warning.csr.pem "
847 "-CA /etc/BastionGuard/certs/BastionGuard-ca.crt.pem -CAkey /etc/BastionGuard/certs/BastionGuard-ca.key.pem "
848 "-CAcreateserial -out /etc/BastionGuard/certs/local-warning.crt.pem -days 825 "
849 "-sha256 -extfile /etc/BastionGuard/certs/v3ext.cnf");
850 cmds.push_back(
"rm -f /etc/BastionGuard/certs/local-warning.csr.pem /etc/BastionGuard/certs/BastionGuard-ca.srl /etc/BastionGuard/certs/v3ext.cnf");
851 cmds.push_back(
"chmod 600 /etc/BastionGuard/certs/*key.pem || true");
852 cmds.push_back(
"chmod 644 /etc/BastionGuard/certs/*.crt.pem || true");
854 cmds.push_back(
"if command -v update-ca-certificates >/dev/null 2>&1; then "
855 "install -m 644 /etc/BastionGuard/certs/BastionGuard-ca.crt.pem /usr/local/share/ca-certificates/BastionGuard-ca.crt && "
856 "update-ca-certificates; "
857 "elif command -v trust >/dev/null 2>&1; then "
858 "trust anchor --store /etc/BastionGuard/certs/BastionGuard-ca.crt.pem; "
859 "elif command -v update-ca-trust >/dev/null 2>&1; then "
860 "install -m 644 /etc/BastionGuard/certs/BastionGuard-ca.crt.pem /etc/pki/ca-trust/source/anchors/BastionGuard-ca.crt && "
861 "update-ca-trust extract; fi");
864 if (has_opt(
"blacklist")) {
865 std::string dst = user_data_dir() +
"/blacklist/phishing-blacklist.txt";
866 cmds.push_back(
_(
"echo '[BastionGuard] Copio blacklist phishing...' >&2"));
867 cmds.push_back(
"mkdir -p " + user_data_dir() +
"/blacklist || true");
868 cmds.push_back(
"install -m 0644 phishing-blacklist.txt " + dst +
" || true");
871 cmds.push_back(
_(
"echo '[BastionGuard] Riavvio BastionGuard-phishing-scanner...' >&2"));
872 cmds.push_back(
"if [ -x /usr/libexec/bastionguard/bastionguard-service ] && "
873 "/usr/libexec/bastionguard/bastionguard-service --system exists BastionGuard-phishing-scanner.service >/dev/null 2>&1; then "
874 "/usr/libexec/bastionguard/bastionguard-service --system restart BastionGuard-phishing-scanner.service || true; "
875 "else echo '[BastionGuard] Nessun servizio BastionGuard-phishing-scanner trovato.' >&2; fi");
881 std::cerr <<
_(
"[wizard] ⚠ Errore nello script privilegiato:\n") << log << std::endl;
884 std::cout <<
_(
"[wizard] ✅ Tutte le operazioni privilegiate completate.") << std::endl;
887 if (has_opt(
"native-host")) {
Result run(const std::vector< std::string > &args, bool merge_stderr=false, std::size_t limit=8U *1024U *1024U, const std::map< std::string, std::string > &environment={}, const std::function< bool(const std::string &)> &on_line={})