807 lines
37 KiB
PHP
807 lines
37 KiB
PHP
<?php
|
|
require_once __DIR__ . '/functions.php';
|
|
|
|
function bg_target_user_record() {
|
|
$user = bg_detect_desktop_user();
|
|
return $user ? bg_user_record($user) : null;
|
|
}
|
|
|
|
function bg_home_path($rel) {
|
|
$rec = bg_target_user_record();
|
|
$home = $rec && !empty($rec['home']) ? rtrim($rec['home'], '/') : rtrim((string)getenv('HOME'), '/');
|
|
if ($home === '') $home = '/tmp';
|
|
return $home . '/' . ltrim($rel, '/');
|
|
}
|
|
|
|
function bg_bastionguard_config_dir($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
$rec = $user ? bg_user_record($user) : null;
|
|
$home = $rec && !empty($rec['home']) ? rtrim($rec['home'], '/') : rtrim((string)getenv('HOME'), '/');
|
|
if ($home === '') $home = '/tmp';
|
|
return $home . '/.config/BastionGuard';
|
|
}
|
|
|
|
function bg_bastionguard_data_dir($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
$rec = $user ? bg_user_record($user) : null;
|
|
$home = $rec && !empty($rec['home']) ? rtrim($rec['home'], '/') : rtrim((string)getenv('HOME'), '/');
|
|
if ($home === '') $home = '/tmp';
|
|
return $home . '/.local/share/BastionGuard';
|
|
}
|
|
|
|
function bg_wizard_services_default() {
|
|
// Same unit set and order as src/first_run/first-run-services-window.cpp.
|
|
return [
|
|
'BastionGuard-ransomware-alert.service' => true,
|
|
'BastionGuard-ransomware-realtime-alert.service' => true,
|
|
'BastionGuard-ransomware-scanner.service' => true,
|
|
'BastionGuard-useragent.service' => true,
|
|
'BastionGuard-phishing-scanner.service' => true,
|
|
'BastionGuard-mailproxy.service' => true,
|
|
'BastionGuard-ransomware-realtime.service' => true,
|
|
];
|
|
}
|
|
|
|
function bg_wizard_service_minimal() {
|
|
// Same minimal preset as FirstRunServicesWindow::select_minimal().
|
|
return [
|
|
'BastionGuard-ransomware-realtime-alert.service' => true,
|
|
];
|
|
}
|
|
|
|
function bg_wizard_service_labels() {
|
|
// Labels/descriptions mirrored from src/first_run/first-run-services-window.cpp.
|
|
return [
|
|
'BastionGuard-ransomware-alert.service' => ['label'=>'Ransomware Alert', 'description'=>'Notifiche su possibili attività ransomware'],
|
|
'BastionGuard-ransomware-realtime-alert.service' => ['label'=>'Ransomware Realtime Alert', 'description'=>'Avvisi in tempo reale su comportamenti sospetti'],
|
|
'BastionGuard-ransomware-scanner.service' => ['label'=>'Ransomware Scanner', 'description'=>'Scansione periodica alla ricerca di comportamenti malevoli'],
|
|
'BastionGuard-useragent.service' => ['label'=>'User-Agent Randomizer', 'description'=>'Modifica user-agent per aumentare anonimato'],
|
|
'BastionGuard-phishing-scanner.service' => ['label'=>'Phishing Scanner (System)', 'description'=>'Scansione anti-phishing a livello di sistema'],
|
|
'BastionGuard-mailproxy.service' => ['label'=>'Mail Proxy', 'description'=>'Proxy SMTP locale per protezione email in uscita'],
|
|
'BastionGuard-ransomware-realtime.service' => ['label'=>'Ransomware Realtime (System)', 'description'=>'Monitor realtime ransomware a livello di sistema'],
|
|
];
|
|
}
|
|
|
|
function bg_wizard_config_array(array $services = null, $installedAt = null) {
|
|
return [
|
|
'installed_at' => $installedAt ?: gmdate('Y-m-d\TH:i:s\Z'),
|
|
'services' => $services ?: bg_wizard_services_default(),
|
|
'wizard_completed' => true,
|
|
];
|
|
}
|
|
|
|
function bg_wizard_config_json(array $services = null) {
|
|
return json_encode(bg_wizard_config_array($services), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
|
|
}
|
|
|
|
function bg_config_catalog() {
|
|
$cfg = bg_bastionguard_config_dir();
|
|
$catalog = [
|
|
'app_config' => [
|
|
'label' => 'General GUI/wizard config',
|
|
'path' => $cfg . '/config.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => bg_wizard_config_array(bg_wizard_services_default(), null),
|
|
'description' => 'Wizard status and initial services read from the desktop UI. The WebUI does not overwrite it if already completed.',
|
|
],
|
|
'config_ini' => [
|
|
'label' => 'Runtime/quarantine config',
|
|
'path' => $cfg . '/config.ini',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => 'quarantinePath=' . bg_bastionguard_data_dir() . '/quarantine' . "\n",
|
|
'description' => 'Runtime config used by Backend::getQuarantinePath()/setQuarantinePath.',
|
|
],
|
|
'cloud' => [
|
|
'label' => 'VirusTotal cloud API',
|
|
'path' => $cfg . '/cloud.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "malware_bazaar_api_key=\n",
|
|
'description' => 'VirusTotal API key read by ScanPage/cloud scanner.',
|
|
],
|
|
'cred' => [
|
|
'label' => 'Credentials / local secrets',
|
|
'path' => $cfg . '/cred.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "# Local BastionGuard credentials\n",
|
|
'description' => 'Local credentials file if used by installed modules.',
|
|
],
|
|
'firewall' => [
|
|
'label' => 'Firewall',
|
|
'path' => $cfg . '/firewall.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "enabled=1\nmode=auto\n",
|
|
'description' => 'Firewall preferences read by firewall modules.',
|
|
],
|
|
'identity_leak' => [
|
|
'label' => 'Identity leaks',
|
|
'path' => $cfg . '/identity_leak.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => ['emails'=>[], 'providers'=>[]],
|
|
'description' => 'Identity/email breach check configuration.',
|
|
],
|
|
'lang' => [
|
|
'label' => 'UI language',
|
|
'path' => $cfg . '/lang.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "language=en\n",
|
|
'description' => 'Language read by multiple desktop components.',
|
|
],
|
|
'mail' => [
|
|
'label' => 'Mail proxy',
|
|
'path' => $cfg . '/mail.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => [
|
|
'version'=>2,
|
|
'enabled'=>false,
|
|
'scan_outgoing'=>true,
|
|
'inject_signature'=>false,
|
|
'local_smtp_host'=>'127.0.0.1',
|
|
'local_smtp_port'=>2525,
|
|
'local_smtp_tls_port'=>2465,
|
|
'local_submission_port'=>2587,
|
|
'advertise_starttls'=>true,
|
|
'enable_implicit_tls_listener'=>true,
|
|
'default_profile_id'=>'default',
|
|
'profiles'=>[[
|
|
'id'=>'default','label'=>'Default','match_from'=>[], 'match_from_domain'=>[],
|
|
'smtp_host'=>'','smtp_port'=>587,'starttls'=>true,'implicit_tls'=>false,'username'=>'','password'=>'',
|
|
'signature'=>['display_name'=>'','job_title'=>'','company'=>'','phone'=>'','website'=>'','logo_path'=>'/usr/share/BastionGuard/data/logo.png']
|
|
]]
|
|
],
|
|
'description' => 'Configuration used by BastionGuard-mailproxy.service.',
|
|
],
|
|
'payments' => [
|
|
'label' => 'Safe payments',
|
|
'path' => $cfg . '/payments.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => [
|
|
'version'=>1,
|
|
'enabled'=>false,
|
|
'list'=>['paypal.com','stripe.com','adyen.com','klarna.com','checkout.com','worldpay.com','braintreepayments.com','braintreegateway.com','amazonpay.com','pay.google.com','pay.apple.com','amazon.com'],
|
|
],
|
|
'description' => 'Payment domain list read by desktop SecureBrowser. The WebUI only saves the list and never manages proxy/browser daemons.',
|
|
],
|
|
'proxy_bypass' => [
|
|
'label' => 'Proxy bypass',
|
|
'path' => $cfg . '/proxy_bypass.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => ['domains'=>['localhost','127.0.0.1']],
|
|
'description' => 'Domains/exclusions used by desktop proxy/browser components. The WebUI only edits the file.',
|
|
],
|
|
'samba' => [
|
|
'label' => 'Samba',
|
|
'path' => $cfg . '/samba.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "enabled=false\n",
|
|
'description' => 'Samba configuration compatible with Backend::saveSambaConfig: first line enabled=true/false, then one path/share per line.',
|
|
],
|
|
'settings' => [
|
|
'label' => 'Settings generali',
|
|
'path' => $cfg . '/settings.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => ['version'=>1],
|
|
'description' => 'Preferenze generali della UI BastionGuard.',
|
|
],
|
|
'theme' => [
|
|
'label' => 'UI theme',
|
|
'path' => $cfg . '/theme.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "theme=system\n",
|
|
'description' => 'Theme read from the desktop UI.',
|
|
],
|
|
'whitelist' => [
|
|
'label' => 'Whitelist browser/phishing',
|
|
'path' => $cfg . '/whitelist.json',
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => ['domains'=>[]],
|
|
'description' => 'Whitelist usata da backend e secure browser.',
|
|
],
|
|
'allowlist' => [
|
|
'label' => 'Anti-Ransomware SHA256 allowlist',
|
|
'path' => $cfg . '/allowlist.txt',
|
|
'scope' => 'user',
|
|
'type' => 'text',
|
|
'default' => '',
|
|
'description' => 'SHA256 allowlist compatible with SettingsPage.cpp.',
|
|
],
|
|
'googlesafe' => [
|
|
'label' => 'Google Safe Browsing',
|
|
'path' => $cfg . '/googlesafe.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "# Google Safe Browsing configuration\ngoogle_safe_enabled=false\ngoogle_safe_key=\n",
|
|
'description' => 'Google Safe Browsing settings used by Anti-Phishing.',
|
|
],
|
|
'first_run_services_done' => [
|
|
'label' => 'Flag first-run services',
|
|
'path' => $cfg . '/first-run-services-done',
|
|
'scope' => 'user',
|
|
'type' => 'text',
|
|
'default' => "",
|
|
'description' => 'Flag creato dal wizard servizi della UI.',
|
|
],
|
|
'webports' => [
|
|
'label' => 'Porte Web BastionGuard',
|
|
'path' => '/etc/BastionGuard/webports.conf',
|
|
'scope' => 'system',
|
|
'type' => 'text',
|
|
'default' => "81 444\n",
|
|
'description' => 'Porte HTTP/HTTPS usate dal wizard e dalla configurazione NGINX.',
|
|
],
|
|
'dnsmasq' => [
|
|
'label' => 'DNSMasq BastionGuard',
|
|
'path' => '/etc/dnsmasq.d/BastionGuard.conf',
|
|
'scope' => 'system',
|
|
'type' => 'text',
|
|
'default' => "# Interfacce di ascolto per BastionGuard DNS Protection\nlisten-address=127.0.0.1,127.0.0.2\n\n# Facoltativo: riduce warning di bind\nbind-interfaces\n",
|
|
'description' => 'Local DNS config compatible with the GTK wizard.',
|
|
],
|
|
'scan_config' => [
|
|
'label' => 'Manual scan/realtime',
|
|
'path' => bg_home_path('.local/share/BastionGuard/config.json'),
|
|
'scope' => 'user',
|
|
'type' => 'json',
|
|
'default' => ['auto_scan_enabled' => true],
|
|
'description' => 'Configuration used by ScanPage.cpp for auto-scan.',
|
|
],
|
|
'scanner' => [
|
|
'label' => 'Anti-Ransomware scanner',
|
|
'path' => $cfg . '/scanner.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "# --- BastionGuard Anti-Ransomware Config ---\nsuspicious_only=0\nignore_paths=/proc;/sys;/dev;/run;/tmp\nignore_ext=.tmp;.log;.cache\nsuspicious_ext=.locked;.encrypted;.crypt;.enc;.encrypted\n\n# --- Ransomware Scanner ---\nenable_yara=1\nenable_sanesecurity=1\nscan_interval=60\nscan_path=/home\n",
|
|
'description' => 'Unifica le chiavi lette da SettingsPage.cpp e dallo scanner ransomware.',
|
|
],
|
|
'sources' => [
|
|
'label' => 'Sorgenti aggiornamenti',
|
|
'path' => $cfg . '/sources.conf',
|
|
'scope' => 'user',
|
|
'type' => 'ini',
|
|
'default' => "clamav=1\nsanesecurity=1\n",
|
|
'description' => 'Sorgenti UpdatePage per firme e feed.',
|
|
],
|
|
];
|
|
foreach ($catalog as $key => &$def) { $def['key'] = $key; }
|
|
unset($def);
|
|
return $catalog;
|
|
}
|
|
|
|
function bg_config_default_string(array $def) {
|
|
$v = $def['default'];
|
|
if (($def['type'] ?? '') === 'directory') return '';
|
|
if (($def['type'] ?? '') === 'json') return json_encode($v, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
|
|
return (string)$v;
|
|
}
|
|
|
|
function bg_config_parse_raw(array $def, array $out) {
|
|
$type = $def['type'] ?? 'text';
|
|
if ($type === 'json') {
|
|
$p = json_decode($out['raw'], true);
|
|
if (json_last_error() === JSON_ERROR_NONE) $out['parsed'] = $p;
|
|
else $out['error'] = 'Invalid JSON: ' . json_last_error_msg();
|
|
} elseif ($type === 'ini') {
|
|
$p = @parse_ini_string($out['raw'], false, INI_SCANNER_RAW);
|
|
$out['parsed'] = is_array($p) ? $p : [];
|
|
} else {
|
|
$out['parsed'] = $out['raw'];
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
function bg_config_read_via_helper(array $def) {
|
|
$key = $def['key'] ?? '';
|
|
if ($key === '') return null;
|
|
$args = ['read-config-b64', $key];
|
|
if (($def['scope'] ?? '') === 'user') {
|
|
$user = bg_detect_desktop_user();
|
|
if (!$user) return null;
|
|
$args[] = $user;
|
|
}
|
|
$res = bg_admin_helper($args, 60);
|
|
$code = (int)($res['code'] ?? 1);
|
|
$out = ['exists'=>false, 'readable'=>false, 'raw'=>'', 'parsed'=>null, 'error'=>'', 'via'=>'helper'];
|
|
if ($code === 66) return $out;
|
|
if ($code === 68) { $out['exists'] = true; $out['readable'] = true; $out['error'] = 'The path is a directory.'; return $out; }
|
|
if ($code === 65) { $out['exists'] = true; $out['readable'] = true; $out['error'] = 'The path exists but is not a file.'; return $out; }
|
|
if ($code === 13) { $out['exists'] = true; $out['readable'] = false; $out['error'] = 'File not readable neppure tramite helper.'; return $out; }
|
|
if ($code !== 0) return null;
|
|
$raw = base64_decode(trim((string)($res['output'] ?? '')), true);
|
|
if ($raw === false) { $out['exists'] = true; $out['readable'] = false; $out['error'] = 'Helper ha restituito dati non base64.'; return $out; }
|
|
$out['exists'] = true;
|
|
$out['readable'] = true;
|
|
$out['raw'] = $raw;
|
|
return bg_config_parse_raw($def, $out);
|
|
}
|
|
|
|
function bg_config_read(array $def) {
|
|
$path = $def['path'];
|
|
$type = $def['type'] ?? 'text';
|
|
$out = ['exists'=>file_exists($path), 'readable'=>is_readable($path), 'raw'=>'', 'parsed'=>null, 'error'=>'', 'via'=>'direct'];
|
|
|
|
if ($type === 'directory') {
|
|
if (is_dir($path)) {
|
|
$items = [];
|
|
foreach (scandir($path) ?: [] as $it) {
|
|
if ($it === '.' || $it === '..') continue;
|
|
$items[] = $it . (is_dir($path . '/' . $it) ? '/' : '');
|
|
}
|
|
$out['exists'] = true; $out['readable'] = is_readable($path); $out['parsed'] = $items; $out['raw'] = implode("\n", $items);
|
|
return $out;
|
|
}
|
|
// La directory potrebbe esistere ma non essere attraversabile da Apache: prova helper.
|
|
$scan = bg_config_scan_dir();
|
|
if (!empty($scan['exists'])) {
|
|
foreach ($scan['items'] as $it) {
|
|
if ($it['name'] === basename($path) && $it['type'] === 'directory') {
|
|
$out['exists'] = true; $out['readable'] = !empty($it['readable']); $out['via'] = 'helper'; return $out;
|
|
}
|
|
}
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
if (!$out['exists'] || !$out['readable'] || !is_file($path)) {
|
|
$helper = bg_config_read_via_helper($def);
|
|
if (is_array($helper)) return $helper;
|
|
if (!$out['exists']) return $out;
|
|
if (!is_file($path)) { $out['error'] = 'The path exists but is not a file.'; return $out; }
|
|
if (!$out['readable']) { $out['error'] = 'File not readable dal processo WebUI e helper non disponibile/aggiornato.'; return $out; }
|
|
}
|
|
|
|
$raw = @file_get_contents($path);
|
|
if ($raw === false) {
|
|
$helper = bg_config_read_via_helper($def);
|
|
if (is_array($helper)) return $helper;
|
|
$out['error'] = 'File read error.'; return $out;
|
|
}
|
|
$out['raw'] = $raw;
|
|
return bg_config_parse_raw($def, $out);
|
|
}
|
|
|
|
function bg_config_direct_write(array $def, $content, $createOnly = false) {
|
|
$path = $def['path'];
|
|
if ($createOnly && is_file($path)) return ['code'=>0,'output'=>'already present'];
|
|
$dir = dirname($path);
|
|
if (!is_dir($dir)) bg_safe_mkdir($dir, 0750);
|
|
if (($def['type'] ?? '') === 'json') {
|
|
json_decode($content, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) return ['code'=>2,'output'=>'Invalid JSON: '.json_last_error_msg()];
|
|
}
|
|
$tmp = $path . '.webui.tmp';
|
|
if (@file_put_contents($tmp, $content, LOCK_EX) === false) return ['code'=>1,'output'=>'Impossibile scrivere file temporaneo.'];
|
|
@chmod($tmp, ($def['scope'] ?? '') === 'system' ? 0644 : 0640);
|
|
if (!@rename($tmp, $path)) { @unlink($tmp); return ['code'=>1,'output'=>'Impossibile spostare il file finale.']; }
|
|
return ['code'=>0,'output'=>'scritto: '.$path];
|
|
}
|
|
|
|
function bg_admin_helper($args, $timeout = 120) {
|
|
$helper = '/usr/local/sbin/bastionguard-webui-admin';
|
|
if (!is_executable($helper)) return ['code'=>127,'output'=>'Admin helper not installed. Run: sudo bash scripts/install-webui-helpers.sh "$USER"'];
|
|
$argv = ['sudo','-n',$helper];
|
|
foreach ($args as $a) $argv[] = (string)$a;
|
|
return bg_run_argv($argv, $timeout);
|
|
}
|
|
|
|
function bg_wizard_helper($args, $timeout = 300) {
|
|
$helper = '/usr/local/sbin/bastionguard-webui-wizard';
|
|
if (!is_executable($helper)) {
|
|
return ['code'=>127,'output'=>'Wizard helper not installed. Run: sudo bash scripts/install-webui-helpers.sh "$USER"'];
|
|
}
|
|
$argv = ['sudo','-n',$helper];
|
|
foreach ($args as $a) $argv[] = (string)$a;
|
|
return bg_run_argv($argv, $timeout);
|
|
}
|
|
|
|
function bg_wizard_status($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
if (!$user) return ['code'=>67,'output'=>'No desktop user configured.'];
|
|
return bg_wizard_helper(['status', $user], 60);
|
|
}
|
|
|
|
function bg_wizard_detect_web() {
|
|
return bg_wizard_helper(['detect-web'], 30);
|
|
}
|
|
|
|
function bg_wizard_apply_web_config($http, $https, $distro = 'auto') {
|
|
$http = (int)$http; $https = (int)$https;
|
|
if ($http < 1 || $http > 65535) $http = 81;
|
|
if ($https < 1 || $https > 65535) $https = 444;
|
|
return bg_wizard_helper(['apply-web-config', (string)$http, (string)$https, (string)$distro], 120);
|
|
}
|
|
|
|
function bg_wizard_install_ca($userOrPath = '') {
|
|
$args = ['install-ca'];
|
|
if (trim((string)$userOrPath) !== '') $args[] = trim((string)$userOrPath);
|
|
return bg_wizard_helper($args, 120);
|
|
}
|
|
|
|
function bg_wizard_install_thunderbird_extension($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
if (!$user) return ['code'=>67,'output'=>'No desktop user configured.'];
|
|
return bg_wizard_helper(['install-thunderbird-extension', $user], 120);
|
|
}
|
|
|
|
function bg_data_file_read_b64($key, $user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
if (!$user) return ['code'=>67,'output'=>'No desktop user configured.'];
|
|
return bg_admin_helper(['read-data-file-b64', $key, $user], 60);
|
|
}
|
|
|
|
function bg_update_banks_user($user = null, $url = '') {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
if (!$user) return ['code'=>67,'output'=>'No desktop user configured.'];
|
|
$args = ['update-banks', $user];
|
|
if (trim((string)$url) !== '') $args[] = trim((string)$url);
|
|
return bg_wizard_helper($args, 180);
|
|
}
|
|
|
|
function bg_config_ensure_all($targetUser = null) {
|
|
if (function_exists('bg_current_user_is_admin') && !bg_current_user_is_admin()) return ['code'=>13,'output'=>bg_t('config_write_denied')];
|
|
$targetUser = $targetUser ?: bg_detect_desktop_user();
|
|
$res = bg_admin_helper(['ensure-configs', $targetUser], 120);
|
|
if (($res['code'] ?? 1) === 0) return $res;
|
|
// Fallback: prova a creare solo i file scrivibili direttamente dal processo PHP.
|
|
$logs = [trim((string)$res['output'])];
|
|
foreach (bg_config_catalog() as $key=>$def) {
|
|
if (in_array($key, ['app_config','first_run_services_done'], true)) continue;
|
|
if (($def['scope'] ?? '') === 'system') continue;
|
|
if (file_exists($def['path'])) continue;
|
|
$r = bg_config_direct_write($def, bg_config_default_string($def), true);
|
|
$logs[] = $key . ': ' . $r['output'];
|
|
}
|
|
return ['code'=>$res['code'] ?? 1, 'output'=>implode("\n", array_filter($logs))];
|
|
}
|
|
|
|
function bg_config_save_key($key, $content) {
|
|
if (function_exists('bg_current_user_is_admin') && !bg_current_user_is_admin()) return ['code'=>13,'output'=>bg_t('config_write_denied')];
|
|
$catalog = bg_config_catalog();
|
|
if (empty($catalog[$key])) return ['code'=>2,'output'=>'Invalid configuration key.'];
|
|
$def = $catalog[$key];
|
|
if (($def['scope'] ?? '') === 'system') {
|
|
return bg_admin_helper(['write-config', $key, base64_encode($content)], 60);
|
|
}
|
|
// If the file is in another user home, use the helper to keep the correct owner.
|
|
$target = bg_detect_desktop_user();
|
|
if ($target && bg_current_process_user() !== $target) return bg_admin_helper(['write-config', $key, base64_encode($content), $target], 60);
|
|
return bg_config_direct_write($def, $content, false);
|
|
}
|
|
|
|
function bg_wizard_steps() {
|
|
return [
|
|
'resolv' => ['label'=>bg_t('wizard_step_resolver'), 'description'=>bg_t('wizard_step_resolver_desc')],
|
|
'dnsmasq' => ['label'=>bg_t('wizard_step_dnsmasq'), 'description'=>bg_t('wizard_step_dnsmasq_desc')],
|
|
'firewall' => ['label'=>bg_t('wizard_step_firewall'), 'description'=>bg_t('wizard_step_firewall_desc')],
|
|
'nftables' => ['label'=>bg_t('wizard_step_nftables'), 'description'=>bg_t('wizard_step_nftables_desc')],
|
|
'certs' => ['label'=>bg_t('wizard_step_certs'), 'description'=>bg_t('wizard_step_certs_desc')],
|
|
'webconf' => ['label'=>bg_t('wizard_step_webserver'), 'description'=>bg_t('wizard_step_webserver_desc')],
|
|
'native' => ['label'=>bg_t('wizard_step_native'), 'description'=>bg_t('wizard_step_native_desc')],
|
|
'useragent' => ['label'=>bg_t('wizard_step_useragent'), 'description'=>bg_t('wizard_step_useragent_desc')],
|
|
'banks' => ['label'=>bg_t('wizard_step_banks'), 'description'=>bg_t('wizard_step_banks_desc')],
|
|
'services' => ['label'=>bg_t('wizard_step_services'), 'description'=>bg_t('wizard_step_services_desc')],
|
|
];
|
|
}
|
|
|
|
function bg_wizard_step_defaults_src() {
|
|
// GTK wizard check buttons start unchecked; these defaults preserve that behavior.
|
|
return [
|
|
'resolv' => false,
|
|
'dnsmasq' => false,
|
|
'firewall' => false,
|
|
'nftables' => false,
|
|
'certs' => false,
|
|
'webconf' => false,
|
|
'native' => false,
|
|
'useragent' => false,
|
|
'banks' => false,
|
|
];
|
|
}
|
|
|
|
function bg_wizard_step_recommended() {
|
|
// Convenience preset used by the WebUI only; not auto-applied.
|
|
return [
|
|
'resolv' => true,
|
|
'dnsmasq' => true,
|
|
'firewall' => true,
|
|
'nftables' => false,
|
|
'certs' => true,
|
|
'webconf' => true,
|
|
'native' => true,
|
|
'useragent' => true,
|
|
'banks' => true,
|
|
];
|
|
}
|
|
|
|
function bg_webui_disabled_unit($unit) {
|
|
return in_array(strtolower((string)$unit), ['bastionguard-cef.service','bastionguard-pacd.service','bastionguard-privacyd.service','bastionguard-usbd.service'], true);
|
|
}
|
|
|
|
function bg_wizard_selected_services_from_post(array $post, array $defaults = null, $prefix = 'svc') {
|
|
$defaults = $defaults ?: bg_wizard_services_default();
|
|
$selected = [];
|
|
$posted = isset($post[$prefix]) && is_array($post[$prefix]) ? $post[$prefix] : [];
|
|
foreach ($defaults as $unit => $enabled) {
|
|
$selected[$unit] = bg_webui_disabled_unit($unit) ? false : !empty($posted[$unit]);
|
|
}
|
|
return $selected;
|
|
}
|
|
|
|
function bg_wizard_selected_steps_from_post(array $post, $prefix = 'step') {
|
|
$selected = [];
|
|
$posted = isset($post[$prefix]) && is_array($post[$prefix]) ? $post[$prefix] : [];
|
|
foreach (array_keys(bg_wizard_steps()) as $key) {
|
|
if ($key === 'services') continue;
|
|
$selected[$key] = !empty($posted[$key]);
|
|
}
|
|
return $selected;
|
|
}
|
|
|
|
function bg_desktop_mode_status($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
if (!$user) return ['code'=>67, 'output'=>'No desktop user configured.', 'mode'=>'unknown', 'summary'=>''];
|
|
$res = bg_admin_helper(['desktop-mode', $user], 30);
|
|
$summary = trim((string)($res['output'] ?? ''));
|
|
$mode = str_starts_with($summary, 'gui') ? 'gui' : (str_starts_with($summary, 'headless') ? 'headless' : 'unknown');
|
|
return ['code'=>$res['code'] ?? 1, 'output'=>$summary, 'mode'=>$mode, 'summary'=>$summary];
|
|
}
|
|
|
|
|
|
function bg_wizard_lock_status($user = null, $scan = null, $configStatus = null, $desktopMode = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
$scan = is_array($scan) ? $scan : bg_config_scan_dir($user ?: null);
|
|
$configStatus = is_array($configStatus) ? $configStatus : bg_wizard_config_status($user ?: null);
|
|
$desktopMode = is_array($desktopMode) ? $desktopMode : bg_desktop_mode_status($user ?: null);
|
|
$reasons = [];
|
|
$items = is_array($scan['items'] ?? null) ? $scan['items'] : [];
|
|
|
|
// Hard boundary: the WebUI wizard is configurable only when the desktop
|
|
// profile has no BastionGuard configuration yet. A running GUI alone is not
|
|
// a lock because CEF/PAC are excluded from the WebUI and remain GTK-owned.
|
|
if (!empty($configStatus['exists'])) {
|
|
$reasons[] = bg_t('wizard_lock_reason_config_json');
|
|
} elseif (!empty($scan['exists']) && count($items) > 0) {
|
|
$sample = array_slice(array_map(function($i){ return (string)($i['name'] ?? ''); }, $items), 0, 6);
|
|
$sample = array_values(array_filter($sample, fn($v) => $v !== ''));
|
|
$detail = $sample ? ' (' . implode(', ', $sample) . (count($items) > count($sample) ? ', …' : '') . ')' : '';
|
|
$reasons[] = bg_t('wizard_lock_reason_config_dir') . $detail;
|
|
}
|
|
return [
|
|
'locked' => count($reasons) > 0,
|
|
'reasons' => $reasons,
|
|
'user' => $user,
|
|
'config_dir' => (string)($scan['dir'] ?? bg_bastionguard_config_dir($user)),
|
|
'desktop_mode' => (string)($desktopMode['mode'] ?? 'unknown'),
|
|
'desktop_summary' => (string)($desktopMode['summary'] ?? ''),
|
|
];
|
|
}
|
|
|
|
function bg_wizard_environment_status($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
$desktop = bg_desktop_mode_status($user);
|
|
$web = bg_web_options();
|
|
return [
|
|
'desktop_user' => $user,
|
|
'desktop_mode' => $desktop['mode'],
|
|
'desktop_summary' => $desktop['summary'],
|
|
'http_port' => (int)($web['http_port'] ?? 81),
|
|
'https_port' => (int)($web['https_port'] ?? 444),
|
|
];
|
|
}
|
|
|
|
function bg_wizard_apply(array $opts) {
|
|
$user = trim((string)($opts['desktop_user'] ?? bg_detect_desktop_user()));
|
|
$http = (int)($opts['http_port'] ?? 81); $https = (int)($opts['https_port'] ?? 444);
|
|
if ($http < 1 || $http > 65535) $http = 81;
|
|
if ($https < 1 || $https > 65535) $https = 444;
|
|
$flags = [];
|
|
foreach (array_keys(bg_wizard_steps()) as $k) if (!empty($opts[$k])) $flags[] = $k;
|
|
return bg_wizard_helper(array_merge(['run', $user, (string)$http, (string)$https], $flags), 300);
|
|
}
|
|
|
|
|
|
function bg_config_scan_dir_via_helper($user = null) {
|
|
$user = $user ?: bg_detect_desktop_user();
|
|
if (!$user) return null;
|
|
$res = bg_admin_helper(['list-configs', $user], 60);
|
|
if ((int)($res['code'] ?? 1) !== 0) return null;
|
|
$dir = bg_bastionguard_config_dir($user);
|
|
$exists = false;
|
|
$items = [];
|
|
foreach (preg_split('/\R+/', trim((string)($res['output'] ?? ''))) as $line) {
|
|
if ($line === '') continue;
|
|
$parts = explode("\t", $line);
|
|
if (($parts[0] ?? '') === 'DIR') {
|
|
$dir = $parts[1] ?? $dir;
|
|
$exists = (($parts[2] ?? '0') === '1');
|
|
} elseif (($parts[0] ?? '') === 'ITEM' && count($parts) >= 6) {
|
|
$name = base64_decode($parts[1], true);
|
|
if ($name === false) continue;
|
|
$items[] = [
|
|
'name' => $name,
|
|
'path' => rtrim($dir, '/') . '/' . $name,
|
|
'type' => $parts[2],
|
|
'readable' => ($parts[3] === '1'),
|
|
'size' => (int)$parts[4],
|
|
'mtime' => (int)$parts[5],
|
|
'via' => 'helper',
|
|
];
|
|
}
|
|
}
|
|
usort($items, function($a, $b) { return strcmp($a['name'], $b['name']); });
|
|
return ['dir'=>$dir, 'exists'=>$exists, 'items'=>$items, 'via'=>'helper'];
|
|
}
|
|
|
|
function bg_config_scan_dir($user = null) {
|
|
$dir = bg_bastionguard_config_dir($user);
|
|
$items = [];
|
|
if (is_dir($dir) && is_readable($dir)) {
|
|
foreach (scandir($dir) ?: [] as $name) {
|
|
if ($name === '.' || $name === '..') continue;
|
|
$path = $dir . '/' . $name;
|
|
$items[] = [
|
|
'name' => $name,
|
|
'path' => $path,
|
|
'type' => is_dir($path) ? 'directory' : 'file',
|
|
'readable' => is_readable($path),
|
|
'size' => is_file($path) ? (int)@filesize($path) : 0,
|
|
'mtime' => @filemtime($path) ?: 0,
|
|
'via' => 'direct',
|
|
];
|
|
}
|
|
usort($items, function($a, $b) { return strcmp($a['name'], $b['name']); });
|
|
return ['dir'=>$dir, 'exists'=>true, 'items'=>$items, 'via'=>'direct'];
|
|
}
|
|
$helper = bg_config_scan_dir_via_helper($user);
|
|
if (is_array($helper)) return $helper;
|
|
return ['dir'=>$dir, 'exists'=>false, 'items'=>$items, 'via'=>'none'];
|
|
}
|
|
|
|
function bg_wizard_config_status($user = null) {
|
|
$catalog = bg_config_catalog();
|
|
$def = $catalog['app_config'];
|
|
if ($user) $def['path'] = bg_bastionguard_config_dir($user) . '/config.json';
|
|
$read = bg_config_read($def);
|
|
$out = [
|
|
'path'=>$def['path'],
|
|
'exists'=>!empty($read['exists']),
|
|
'readable'=>!empty($read['readable']),
|
|
'valid'=>false,
|
|
'completed'=>false,
|
|
'services_ok'=>false,
|
|
'data'=>null,
|
|
'raw'=>(string)($read['raw'] ?? ''),
|
|
'error'=>(string)($read['error'] ?? ''),
|
|
'via'=>$read['via'] ?? 'direct',
|
|
];
|
|
if (!$out['exists'] || !$out['readable'] || $out['error']) return $out;
|
|
$data = $read['parsed'];
|
|
if (!is_array($data)) { $out['error'] = 'Invalid JSON or not readable.'; return $out; }
|
|
$out['valid'] = true;
|
|
$out['data'] = $data;
|
|
$out['completed'] = !empty($data['wizard_completed']);
|
|
$services = $data['services'] ?? null;
|
|
$default = bg_wizard_services_default();
|
|
$out['services_ok'] = is_array($services) && count(array_intersect_key($default, $services)) === count($default);
|
|
return $out;
|
|
}
|
|
|
|
function bg_write_first_run_config($user, array $services) {
|
|
$status = bg_wizard_config_status($user);
|
|
if (!empty($status['completed']) && !empty($status['services_ok'])) {
|
|
return ['code'=>0, 'output'=>'config.json already completed: no changes.'];
|
|
}
|
|
$json = bg_wizard_config_json($services);
|
|
$res = bg_admin_helper(['first-run-config', $user, base64_encode($json)], 60);
|
|
if (($res['code'] ?? 1) === 0) return $res;
|
|
|
|
$def = ['path'=>bg_bastionguard_config_dir($user) . '/config.json', 'scope'=>'user', 'type'=>'json'];
|
|
$r = bg_config_direct_write($def, $json, false);
|
|
if (($r['code'] ?? 1) === 0) return $r;
|
|
return ['code'=>$res['code'] ?? 1, 'output'=>trim(($res['output'] ?? '') . "\n" . ($r['output'] ?? ''))];
|
|
}
|
|
|
|
function bg_apply_services_from_config(array $services) {
|
|
$catalog = bg_services_catalog();
|
|
$byUnit = [];
|
|
foreach ($catalog as $svc) foreach ($svc['services'] as $unit) $byUnit[$unit] = $svc;
|
|
$logs = [];
|
|
foreach ($services as $unit=>$enabled) {
|
|
if (bg_webui_disabled_unit($unit)) {
|
|
$logs[] = $unit . ': skipped - CEF/PAC, Webcam/Privacy and USB are GTK/desktop-only or hardware-local modules and are never managed by the server WebUI. [code 0]';
|
|
continue;
|
|
}
|
|
$svc = $byUnit[$unit] ?? ['services'=>[$unit], 'user'=>!in_array($unit, ['BastionGuard-phishing-scanner.service','BastionGuard-ransomware-realtime.service'], true)];
|
|
$action = $enabled ? 'enable' : 'disable';
|
|
$r = bg_service_action([$unit], $action, !empty($svc['user']));
|
|
$logs[] = $unit . ' ' . $action . ': ' . trim($r['output'] ?? '') . ' [code ' . ($r['code'] ?? '?') . ']';
|
|
}
|
|
return ['code'=>0, 'output'=>implode("
|
|
", $logs)];
|
|
}
|
|
|
|
function bg_prediction_items() {
|
|
$items = [];
|
|
$catalog = bg_config_catalog();
|
|
foreach ($catalog as $key=>$def) {
|
|
$r = bg_config_read($def);
|
|
if (!$r['exists']) $items[] = ['level'=>'warning','title'=>'Missing configuration: '.$def['label'], 'detail'=>$def['path'], 'fix'=>'Create missing configurations from the Configurations page.'];
|
|
elseif (!$r['readable']) $items[] = ['level'=>'danger','title'=>'Configuration not readable: '.$def['label'], 'detail'=>$def['path'], 'fix'=>'Check permissions or install the WebUI helper.'];
|
|
elseif ($r['error']) $items[] = ['level'=>'danger','title'=>'Invalid configuration: '.$def['label'], 'detail'=>$r['error'], 'fix'=>'Open and save the file from the Configurations page.'];
|
|
}
|
|
$diag = bg_user_services_diagnostics();
|
|
if (empty($diag['target_user'])) $items[] = ['level'=>'danger','title'=>'Desktop user not detected', 'detail'=>'systemctl --user services will not be readable.', 'fix'=>'Set the user in Settings → Web Options.'];
|
|
if (empty($diag['helper_installed'])) $items[] = ['level'=>'warning','title'=>'Admin/user helper not installed', 'detail'=>'The WebUI cannot create files in /etc or reliably manage systemd --user.', 'fix'=>'sudo bash scripts/install-webui-helpers.sh "$USER"'];
|
|
foreach (bg_services_catalog() as $svc) {
|
|
$st = bg_service_status($svc['services'], $svc['user']);
|
|
if (empty($st['exists'])) $items[] = ['level'=>'secondary','title'=>'Service not found: '.$svc['label'], 'detail'=>implode(', ', $svc['services']), 'fix'=>'Install the package/service or verify the unit name.'];
|
|
elseif (!$st['active']) $items[] = ['level'=>'warning','title'=>'Service fermo: '.$svc['label'], 'detail'=>$st['name'].' → '.$st['active_state'].'/'.$st['sub_state'], 'fix'=>'Startlo dalla pagina Services o dal Wizard.'];
|
|
}
|
|
if (!is_file('/etc/BastionGuard/certs/BastionGuard-ca.crt.pem')) $items[] = ['level'=>'warning','title'=>'CA BastionGuard non generata', 'detail'=>'/etc/BastionGuard/certs/BastionGuard-ca.crt.pem missing.', 'fix'=>'Esegui step Certificati dal Wizard Web.'];
|
|
if (empty($items)) $items[] = ['level'=>'success','title'=>'No obvious issue', 'detail'=>'Main configurations and services look consistent.', 'fix'=>''];
|
|
return $items;
|
|
}
|
|
|
|
|
|
function bg_samba_load_config() {
|
|
$catalog = bg_config_catalog();
|
|
$def = $catalog['samba'];
|
|
$r = bg_config_read($def);
|
|
$enabled = false;
|
|
$shares = [];
|
|
$raw = (string)($r['raw'] ?? '');
|
|
foreach (preg_split('/\R+/', $raw) as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || strpos($line, '#') === 0) continue;
|
|
if (preg_match('/^enabled\s*=\s*(true|1|yes|on)$/i', $line)) { $enabled = true; continue; }
|
|
if (preg_match('/^enabled\s*=\s*(false|0|no|off)$/i', $line)) { $enabled = false; continue; }
|
|
if (preg_match('/^(scan_enabled|quarantine_enabled)\s*=/i', $line)) continue;
|
|
$shares[] = $line;
|
|
}
|
|
return ['enabled'=>$enabled, 'shares'=>$shares, 'read'=>$r, 'path'=>$def['path']];
|
|
}
|
|
|
|
function bg_samba_save_config($enabled, array $shares) {
|
|
$lines = ['enabled=' . ($enabled ? 'true' : 'false')];
|
|
foreach ($shares as $share) {
|
|
$share = trim((string)$share);
|
|
if ($share !== '') $lines[] = $share;
|
|
}
|
|
return bg_config_save_key('samba', implode("\n", $lines) . "\n");
|
|
}
|
|
|
|
function bg_samba_credentials_summary() {
|
|
$catalog = bg_config_catalog();
|
|
$def = $catalog['cred'];
|
|
$r = bg_config_read($def);
|
|
$items = [];
|
|
$current = '';
|
|
foreach (preg_split('/\R+/', (string)($r['raw'] ?? '')) as $line) {
|
|
$line = trim($line);
|
|
if ($line === '') continue;
|
|
if (preg_match('/^\[(.+)\]$/', $line, $m)) {
|
|
$current = $m[1];
|
|
if (!isset($items[$current])) $items[$current] = ['user'=>'', 'has_pass'=>false];
|
|
} elseif ($current !== '' && strpos($line, 'user=') === 0) {
|
|
$u = base64_decode(substr($line, 5), true);
|
|
$items[$current]['user'] = $u === false ? '' : $u;
|
|
} elseif ($current !== '' && strpos($line, 'pass=') === 0) {
|
|
$items[$current]['has_pass'] = trim(substr($line, 5)) !== '';
|
|
}
|
|
}
|
|
return ['items'=>$items, 'read'=>$r, 'path'=>$def['path']];
|
|
}
|
|
|
|
?>
|