Skip to content
API Documentation
BastionGuard™
bastionguard_cef.cpp File Reference

Go to the source code of this file.

Classes

struct  SessionEntry
struct  TunnelSession
struct  ParsedRequest
class  BastionApp

Macros

#define LOCALEDIR   "/usr/share/BastionGuard/locale"
#define BG_WARNING_HTTP_PORT   81
#define BG_WARNING_HTTPS_PORT   444
#define BG_WARNING_ADDR   "127.0.0.2"

Typedefs

using tcp = asio::ip::tcp
using Clock = std::chrono::steady_clock

Functions

static std::string root_domain (const std::string &host)
static void sandbox_close (const std::string &root)
static void sandbox_bind_pid (const std::string &root, pid_t pid)
static void sandbox_close_if_pid (const std::string &root, pid_t pid)
static bool session_active (const std::string &root, std::string &out_type)
static bool session_try_open (const std::string &root, const std::string &type)
static void session_bind_pid (const std::string &root, pid_t pid)
static void session_close (const std::string &root)
static void session_close_if_pid (const std::string &root, pid_t pid)
static void launch_sandbox (const std::string &url, bool is_bank, const std::string &root)
static bool parse_request (const std::string &first_line, ParsedRequest &out)
static void async_tunnel (tcp::socket sock, const std::string &host, const std::string &port)
static void async_forward_http (tcp::socket sock, const ParsedRequest &req, const std::string &full_request)
static std::string warning_url_for (const std::string &host, const std::string &type)
static std::string local_warning_url_for (const std::string &host, const std::string &type)
static void open_notify_window (const std::string &root, const std::string &host, const std::string &type, int argc, char **argv)
static bool sandbox_try_open (const std::string &root)
static void block_active_session (std::shared_ptr< tcp::socket > sock, const ParsedRequest &req, const std::string &type)
static void block_secure_request (std::shared_ptr< tcp::socket > sock, const ParsedRequest &req, const std::string &type, const std::string &root, int argc, char **argv)
static void handle_client (tcp::socket client, int argc, char **argv)
static void do_accept (tcp::acceptor &acceptor, int argc, char **argv)
static bool running_confined ()
static bool parse_args (int argc, char **argv, std::string &listen_host, int &listen_port)
int main (int argc, char **argv)

Variables

static constexpr auto SESSION_TTL = std::chrono::minutes(30)
static const unsigned int IO_THREADS
static asio::io_context g_io {static_cast<int>(IO_THREADS)}
static std::mutex g_session_mutex
static std::unordered_map< std::string, SessionEntry > g_sessions
static std::mutex g_notify_mutex
static std::unordered_set< std::string > g_notify_open
static std::mutex g_sandbox_mutex
static std::unordered_set< std::string > g_sandbox_open
static std::unordered_map< std::string, pid_t > g_sandbox_pid

Macro Definition Documentation

◆ BG_WARNING_ADDR

#define BG_WARNING_ADDR   "127.0.0.2"

Definition at line 76 of file bastionguard_cef.cpp.

Referenced by local_warning_url_for(), and main().

◆ BG_WARNING_HTTP_PORT

#define BG_WARNING_HTTP_PORT   81

Definition at line 70 of file bastionguard_cef.cpp.

Referenced by local_warning_url_for(), and main().

◆ BG_WARNING_HTTPS_PORT

#define BG_WARNING_HTTPS_PORT   444

Definition at line 73 of file bastionguard_cef.cpp.

Referenced by main().

◆ LOCALEDIR

#define LOCALEDIR   "/usr/share/BastionGuard/locale"

Definition at line 67 of file bastionguard_cef.cpp.

Referenced by bastionguard_mo_exists(), init_i18n(), main(), main(), and main().

Typedef Documentation

◆ Clock

using Clock = std::chrono::steady_clock

Definition at line 86 of file bastionguard_cef.cpp.

◆ tcp

using tcp = asio::ip::tcp

Definition at line 85 of file bastionguard_cef.cpp.

Function Documentation

◆ async_forward_http()

void async_forward_http ( tcp::socket sock,
const ParsedRequest & req,
const std::string & full_request )
static

Definition at line 385 of file bastionguard_cef.cpp.

388{
389 std::string rewritten = req.method + " " + req.path + " HTTP/1.1\r\n";
390
391 static const std::vector<std::string> strip_headers = {
392 "via:",
393 "x-forwarded-for:",
394 "x-forwarded-host:",
395 "x-forwarded-proto:",
396 "x-real-ip:",
397 "forwarded:",
398 "proxy-connection:",
399 "proxy-authorization:",
400 "proxy-authenticate:",
401 };
402
403 auto header_should_strip = [](const std::string& line) -> bool {
404 std::string lower = line;
405 for (auto& c : lower) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
406 for (const auto& h : strip_headers) {
407 if (lower.rfind(h, 0) == 0) return true;
408 }
409 return false;
410 };
411
412 const auto first_crlf = full_request.find("\r\n");
413 if (first_crlf != std::string::npos) {
414 std::istringstream ss(full_request.substr(first_crlf + 2));
415 std::string hdr_line;
416 while (std::getline(ss, hdr_line)) {
417 if (!hdr_line.empty() && hdr_line.back() == '\r')
418 hdr_line.pop_back();
419 if (hdr_line.empty()) {
420 rewritten += "\r\n";
421 break;
422 }
423 if (!header_should_strip(hdr_line))
424 rewritten += hdr_line + "\r\n";
425 }
426 } else {
427 rewritten += "\r\n";
428 }
429
430 auto resolver = std::make_shared<tcp::resolver>(g_io);
431 auto psock = std::make_shared<tcp::socket>(std::move(sock));
432 auto req_buf = std::make_shared<std::string>(std::move(rewritten));
433
434 resolver->async_resolve(req.host, req.port,
435 [resolver, psock, req_buf](boost::system::error_code ec, tcp::resolver::results_type eps) mutable {
436 if (ec) {
437 static constexpr char bad[] =
438 "HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n";
439 asio::async_write(*psock, asio::buffer(bad, sizeof(bad) - 1), [psock](auto, auto) {});
440 return;
441 }
442
443 auto remote = std::make_shared<tcp::socket>(g_io);
444 asio::async_connect(*remote, eps,
445 [remote, psock, req_buf](boost::system::error_code ec, const tcp::endpoint&) mutable {
446 if (ec) {
447 static constexpr char bad[] =
448 "HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n";
449 asio::async_write(*psock, asio::buffer(bad, sizeof(bad) - 1), [psock](auto, auto) {});
450 return;
451 }
452
453 asio::async_write(*remote, asio::buffer(*req_buf),
454 [remote, psock, req_buf](boost::system::error_code ec, std::size_t) mutable {
455 if (ec) return;
456 auto session = std::make_shared<TunnelSession>(std::move(*psock), std::move(*remote));
457 session->start();
458 });
459 });
460 });
461}
static asio::io_context g_io

References async_forward_http(), g_io, ParsedRequest::host, ParsedRequest::method, ParsedRequest::path, and ParsedRequest::port.

Referenced by async_forward_http(), and handle_client().

◆ async_tunnel()

void async_tunnel ( tcp::socket sock,
const std::string & host,
const std::string & port )
static

Definition at line 348 of file bastionguard_cef.cpp.

348 {
349 auto resolver = std::make_shared<tcp::resolver>(g_io);
350 auto psock = std::make_shared<tcp::socket>(std::move(sock));
351
352 resolver->async_resolve(host, port,
353 [resolver, psock](boost::system::error_code ec, tcp::resolver::results_type eps) mutable {
354 if (ec) {
355 static constexpr char bad[] =
356 "HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n";
357 asio::async_write(*psock, asio::buffer(bad, sizeof(bad) - 1), [psock](auto, auto) {});
358 return;
359 }
360
361 auto remote = std::make_shared<tcp::socket>(g_io);
362 asio::async_connect(*remote, eps,
363 [remote, psock](boost::system::error_code ec, const tcp::endpoint&) mutable {
364 if (ec) {
365 static constexpr char bad[] =
366 "HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n";
367 asio::async_write(*psock, asio::buffer(bad, sizeof(bad) - 1), [psock](auto, auto) {});
368 return;
369 }
370
371 static constexpr char ok[] =
372 "HTTP/1.1 200 Connection Established\r\n\r\n";
373
374 auto ok_buf = std::make_shared<std::string>(ok);
375 asio::async_write(*psock, asio::buffer(*ok_buf),
376 [ok_buf, psock, remote](boost::system::error_code ec, std::size_t) mutable {
377 if (ec) return;
378 auto session = std::make_shared<TunnelSession>(std::move(*psock), std::move(*remote));
379 session->start();
380 });
381 });
382 });
383}

References async_tunnel(), and g_io.

Referenced by async_tunnel(), and handle_client().

◆ block_active_session()

void block_active_session ( std::shared_ptr< tcp::socket > sock,
const ParsedRequest & req,
const std::string & type )
static

Definition at line 541 of file bastionguard_cef.cpp.

544{
545 if (req.is_connect) {
546 static constexpr char ok200[] =
547 "HTTP/1.1 200 Connection Established\r\n\r\n";
548
549 const std::string intercept_host = req.host;
550 const std::string intercept_type = type;
551
552 auto ok_buf = std::make_shared<std::string>(ok200);
553 asio::async_write(*sock, asio::buffer(*ok_buf),
554 [sock, ok_buf, intercept_host, intercept_type](
555 boost::system::error_code ec, std::size_t) mutable {
556 if (ec) return;
557 std::thread([s = std::move(*sock),
558 host = intercept_host,
559 t = intercept_type]() mutable {
560 // Nessun on_url_ready: la sandbox è già aperta.
561 TlsIntercept::do_intercept(std::move(s), host, t);
562 }).detach();
563 });
564 } else {
565 const std::string loc = warning_url_for(req.host, type);
566 auto redir = std::make_shared<std::string>(
567 "HTTP/1.1 302 Found\r\n"
568 "Cache-Control: no-store\r\n"
569 "Pragma: no-cache\r\n"
570 "Location: " + loc + "\r\n"
571 "Connection: close\r\n\r\n");
572 asio::async_write(*sock, asio::buffer(*redir), [sock, redir](auto, auto) {});
573 }
574}
static std::string warning_url_for(const std::string &host, const std::string &type)
static void do_intercept(tcp::socket sock, const std::string &host, const std::string &type, std::function< void(const std::string &)> on_url_ready=nullptr)

References block_active_session(), ParsedRequest::host, ParsedRequest::is_connect, and warning_url_for().

Referenced by block_active_session(), and handle_client().

◆ block_secure_request()

void block_secure_request ( std::shared_ptr< tcp::socket > sock,
const ParsedRequest & req,
const std::string & type,
const std::string & root,
int argc,
char ** argv )
static

Definition at line 576 of file bastionguard_cef.cpp.

581{
582 if (req.is_connect) {
583 static constexpr char ok200[] =
584 "HTTP/1.1 200 Connection Established\r\n\r\n";
585
586 const std::string intercept_host = req.host;
587 const std::string intercept_type = type;
588 const bool is_bank_type = (type == "bank");
589
590 auto ok_buf = std::make_shared<std::string>(ok200);
591 asio::async_write(*sock, asio::buffer(*ok_buf),
592 [sock, ok_buf, intercept_host, intercept_type, is_bank_type, root, argc, argv](
593 boost::system::error_code ec, std::size_t) mutable {
594 if (ec) return;
595
596 std::thread([s = std::move(*sock),
597 host = intercept_host,
598 t = intercept_type,
599 bank = is_bank_type,
600 r = root,
601 argc, argv]() mutable {
602 auto on_url_ready = [bank, r, t, host, argc, argv](const std::string& full_url) {
603 // Questo callback viene invocato SOLO da do_intercept
604 // per top-level GET/HEAD. Iframe/fetch/POST vengono
605 // inoltrati in-place e non devono aprire una sandbox.
606 if (!session_try_open(r, t)) return;
607
608 open_notify_window(r, host, t, argc, argv);
609
610 if (!sandbox_try_open(r)) {
611 // Stato incoerente/stale: non lasciare una sessione
612 // attiva senza un browser reale.
613 session_close(r);
614 return;
615 }
616
617 launch_sandbox(full_url, bank, r);
618 };
619 TlsIntercept::do_intercept(std::move(s), host, t,
620 std::move(on_url_ready));
621 }).detach();
622 });
623 } else {
624 const std::string full_url = "https://" + req.host + req.path;
625 if (session_try_open(root, type)) {
626 open_notify_window(root, req.host, type, argc, argv);
627 if (sandbox_try_open(root)) {
628 std::thread([full_url, type, root]() {
629 launch_sandbox(full_url, type == "bank", root);
630 }).detach();
631 } else {
632 session_close(root);
633 }
634 }
635
636 const std::string loc = warning_url_for(req.host, type);
637 auto redir = std::make_shared<std::string>(
638 "HTTP/1.1 302 Found\r\n"
639 "Cache-Control: no-store\r\n"
640 "Pragma: no-cache\r\n"
641 "Location: " + loc + "\r\n"
642 "Connection: close\r\n\r\n");
643 asio::async_write(*sock, asio::buffer(*redir), [sock, redir](auto, auto) {});
644 }
645}
static bool session_try_open(const std::string &root, const std::string &type)
static bool sandbox_try_open(const std::string &root)
static void launch_sandbox(const std::string &url, bool is_bank, const std::string &root)
static void open_notify_window(const std::string &root, const std::string &host, const std::string &type, int argc, char **argv)
static void session_close(const std::string &root)

References block_secure_request(), ParsedRequest::host, ParsedRequest::is_connect, launch_sandbox(), open_notify_window(), ParsedRequest::path, sandbox_try_open(), session_close(), session_try_open(), and warning_url_for().

Referenced by block_secure_request(), and handle_client().

◆ do_accept()

void do_accept ( tcp::acceptor & acceptor,
int argc,
char ** argv )
static

Definition at line 776 of file bastionguard_cef.cpp.

776 {
777 acceptor.async_accept(g_io,
778 [&acceptor, argc, argv](boost::system::error_code ec, tcp::socket client) {
779 if (ec == asio::error::operation_aborted) return;
780
781 if (!ec) {
782 handle_client(std::move(client), argc, argv);
783 } else {
784 char buf[256];
785 std::snprintf(buf, sizeof(buf), _("[cef] accept error: %s\n"), ec.message().c_str());
786 std::cerr << buf;
787 }
788
789 do_accept(acceptor, argc, argv);
790 });
791}
#define _(s)
static void do_accept(tcp::acceptor &acceptor, int argc, char **argv)
static void handle_client(tcp::socket client, int argc, char **argv)

References _, do_accept(), g_io, and handle_client().

Referenced by do_accept(), and main().

◆ handle_client()

void handle_client ( tcp::socket client,
int argc,
char ** argv )
static

Definition at line 647 of file bastionguard_cef.cpp.

647 {
648 auto sbuf = std::make_shared<asio::streambuf>();
649 auto sock = std::make_shared<tcp::socket>(std::move(client));
650
651 asio::async_read_until(*sock, *sbuf, "\r\n\r\n",
652 [sock, sbuf, argc, argv](boost::system::error_code ec, std::size_t) mutable {
653 if (ec) return;
654
655 std::string full_request{
656 asio::buffers_begin(sbuf->data()),
657 asio::buffers_end(sbuf->data())
658 };
659
660 std::istringstream is(full_request);
661 std::string first_line;
662 std::getline(is, first_line);
663 if (!first_line.empty() && first_line.back() == '\r')
664 first_line.pop_back();
665
666 ParsedRequest req;
667 if (!parse_request(first_line, req)) {
668 static constexpr char resp[] =
669 "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n";
670 asio::async_write(*sock, asio::buffer(resp, sizeof(resp) - 1), [sock](auto, auto) {});
671 return;
672 }
673
674 // -----------------------------------------------------------------
675 // FAST-PATH: traffico LAN/loopback non passa MAI dal MITM CEF.
676 //
677 // Anche se il browser è configurato col proxy del sistema, per:
678 // - 127.0.0.0/8, 10.0.0.0/8, 172.16/12, 192.168/16,
679 // 169.254/16, 100.64/10, ::1, fe80::/10, fc00::/7
680 // - hostname *.local, *.lan, *.intranet, *.internal, ...
681 // - hostname brevi senza punti (router, nas, printer)
682 // facciamo tunnel diretto e basta. Niente lookup banca/payment,
683 // niente notify, niente block: il traffico interno alla LAN
684 // resta affare dell'utente, BastionGuard non lo vede.
685 // -----------------------------------------------------------------
687 {
688 char dbg[256];
689 std::snprintf(dbg, sizeof(dbg),
690 "[bastionguard] LAN/loopback bypass: %s\n",
691 req.host.c_str());
692 std::cerr << dbg;
693 }
694 if (req.is_connect)
695 async_tunnel(std::move(*sock), req.host, req.port);
696 else
697 async_forward_http(std::move(*sock), req, full_request);
698 return;
699 }
700
701 const std::string root = root_domain(req.host);
702 const bool bypass = domain_filter().is_proxy_bypass(req.host);
703 const bool secure = domain_filter().is_secure(req.host);
704 const bool bank = domain_filter().is_bank(req.host);
705
706 {
707 char dbg[256];
708 std::snprintf(dbg, sizeof(dbg),
709 "[bastionguard] request host=%s bypass=%d intercept=%d bank=%d method=%s\n",
710 req.host.c_str(),
711 static_cast<int>(bypass),
712 static_cast<int>(secure),
713 static_cast<int>(bank),
714 req.method.c_str());
715 std::cerr << dbg;
716 }
717
718 if (bypass) {
719 {
720 char dbg[256];
721 std::snprintf(dbg, sizeof(dbg),
722 "[bastionguard] PROXY-BYPASS (user configured): %s\n",
723 req.host.c_str());
724 std::cerr << dbg;
725 }
726 if (req.is_connect)
727 async_tunnel(std::move(*sock), req.host, req.port);
728 else
729 async_forward_http(std::move(*sock), req, full_request);
730 return;
731 }
732
733 std::string active_type;
734 const bool has_active_session = session_active(root, active_type);
735 if (!secure) {
736 {
737 char dbg[256];
738 std::snprintf(dbg, sizeof(dbg),
739 "[bastionguard] TUNNEL-ONLY (no MITM): %s\n",
740 req.host.c_str());
741 std::cerr << dbg;
742 }
743 if (req.is_connect)
744 async_tunnel(std::move(*sock), req.host, req.port);
745 else
746 async_forward_http(std::move(*sock), req, full_request);
747 return;
748 }
749 if (has_active_session) {
750 char msg[256];
751 std::snprintf(msg, sizeof(msg),
752 _("[bastionguard] secure domain already active, blocking again: %s (session: %s)\n"),
753 req.host.c_str(), root.c_str());
754 std::cerr << msg;
755 block_active_session(sock, req, active_type);
756 return;
757 }
758 const std::string type = bank ? "bank" : "payment";
759
760 {
761 char msg[256];
762 std::snprintf(msg, sizeof(msg),
763 _("[bastionguard] %s intercepted: %s (root: %s)\n"),
764 type.c_str(), req.host.c_str(), root.c_str());
765 std::cerr << msg;
766 }
767
768 // Non aprire ancora sessione/gate: per HTTPS dobbiamo prima
769 // vedere i Fetch Metadata dentro TLS e distinguere top-level da
770 // iframe/POST. Solo la vera navigazione top-level apre CEF.
771 block_secure_request(sock, req, type, root, argc, argv);
772 });
773}
DomainFilter & domain_filter()
static void block_active_session(std::shared_ptr< tcp::socket > sock, const ParsedRequest &req, const std::string &type)
static void async_tunnel(tcp::socket sock, const std::string &host, const std::string &port)
static void async_forward_http(tcp::socket sock, const ParsedRequest &req, const std::string &full_request)
static bool session_active(const std::string &root, std::string &out_type)
static std::string root_domain(const std::string &host)
static void block_secure_request(std::shared_ptr< tcp::socket > sock, const ParsedRequest &req, const std::string &type, const std::string &root, int argc, char **argv)
static bool parse_request(const std::string &first_line, ParsedRequest &out)
bool is_secure(const std::string &host) const
bool is_bank(const std::string &host) const
static bool is_local_network(const std::string &host_raw)
bool is_proxy_bypass(const std::string &host) const

References _, async_forward_http(), async_tunnel(), block_active_session(), block_secure_request(), domain_filter(), handle_client(), ParsedRequest::host, DomainFilter::is_bank(), ParsedRequest::is_connect, DomainFilter::is_local_network(), DomainFilter::is_proxy_bypass(), DomainFilter::is_secure(), ParsedRequest::method, parse_request(), ParsedRequest::port, root_domain(), and session_active().

Referenced by accept_loop(), do_accept(), and handle_client().

◆ launch_sandbox()

void launch_sandbox ( const std::string & url,
bool is_bank,
const std::string & root )
static

Definition at line 233 of file bastionguard_cef.cpp.

235 {
236 const std::string binary = is_bank ? "/usr/bin/BastionGuard-bankopener"
237 : "/usr/bin/BastionGuard-secure";
238
239 // Mantieni il processo reale come figlio del proxy. In questo modo il
240 // proxy conosce il PID della sandbox e può liberare sessione/gate appena
241 // l'utente chiude il Secure Browser. waitpid viene eseguito in un thread
242 // dedicato, quindi nessun io-thread asio viene bloccato.
243 pid_t pid = fork();
244 if (pid < 0) {
245 std::cerr << _("[bastionguard] fork() fallita per launch_sandbox\n");
246 sandbox_close(root);
247 session_close(root);
248 return;
249 }
250
251 if (pid == 0) {
252 setenv("BG_SANDBOX", "1", 1);
253 execl(binary.c_str(), binary.c_str(), url.c_str(), (char*)nullptr);
254 _exit(127);
255 }
256
257 // Associa il PID alla generazione corrente della sessione/gate PRIMA di
258 // avviare il watcher. Le varianti *_if_pid evitano che un vecchio processo
259 // possa chiudere per errore una sessione nuova dello stesso dominio.
260 session_bind_pid(root, pid);
261 sandbox_bind_pid(root, pid);
262
263 {
264 char msg[384];
265 std::snprintf(msg, sizeof(msg),
266 _("[bastionguard] secure browser started: pid=%ld root=%s binary=%s\n"),
267 static_cast<long>(pid), root.c_str(), binary.c_str());
268 std::cerr << msg;
269 }
270
271 std::thread([pid, root]() {
272 int status = 0;
273 pid_t rc;
274 do {
275 rc = waitpid(pid, &status, 0);
276 } while (rc < 0 && errno == EINTR);
277
278 if (rc < 0) {
279 const int wait_err = errno;
280 char msg[320];
281 std::snprintf(msg, sizeof(msg),
282 _("[bastionguard] waitpid(%ld) failed for root %s: %s; uso TTL fallback\n"),
283 static_cast<long>(pid), root.c_str(), std::strerror(wait_err));
284 std::cerr << msg;
285 // Non liberare alla cieca: se non siamo riusciti a osservare la
286 // morte del figlio, il vecchio TTL resta il fallback sicuro.
287 return;
288 }
289
290 // Prima libera il gate e poi la sessione. Se Firefox ritenta proprio
291 // in questa piccola finestra, vede ancora la sessione attiva e non può
292 // creare un doppio browser. Subito dopo la sessione viene rimossa e il
293 // tentativo successivo riapre normalmente la sandbox.
294 sandbox_close_if_pid(root, pid);
295 session_close_if_pid(root, pid);
296 }).detach();
297}
static void session_close_if_pid(const std::string &root, pid_t pid)
static void sandbox_bind_pid(const std::string &root, pid_t pid)
static void sandbox_close(const std::string &root)
static void session_bind_pid(const std::string &root, pid_t pid)
static void sandbox_close_if_pid(const std::string &root, pid_t pid)

References _, launch_sandbox(), sandbox_bind_pid(), sandbox_close(), sandbox_close_if_pid(), session_bind_pid(), session_close(), and session_close_if_pid().

Referenced by block_secure_request(), and launch_sandbox().

◆ local_warning_url_for()

std::string local_warning_url_for ( const std::string & host,
const std::string & type )
static

Definition at line 467 of file bastionguard_cef.cpp.

467 {
468 return std::string("http://") + BG_WARNING_ADDR + ":"
469 + std::to_string(BG_WARNING_HTTP_PORT)
470 + "/" + host + "?type=" + type;
471}
#define BG_WARNING_HTTP_PORT
#define BG_WARNING_ADDR

References BG_WARNING_ADDR, BG_WARNING_HTTP_PORT, and local_warning_url_for().

Referenced by local_warning_url_for().

◆ main()

int main ( int argc,
char ** argv )

Definition at line 955 of file bastionguard_cef.cpp.

955 {
957 std::cerr << "[bastionguard-cef] CEF disabilitato su questa build o distribuzione.\n";
958 return 78;
959 }
960
962
963 bindtextdomain("bastionguard", LOCALEDIR);
964 bind_textdomain_codeset("bastionguard", "UTF-8");
965 textdomain("bastionguard");
966
967 CefMainArgs main_args(argc, argv);
968 CefRefPtr<CefApp> app = new BastionApp();
969
970 const int exit_code = CefExecuteProcess(main_args, app, nullptr);
971 if (exit_code >= 0) return exit_code;
972
973 std::string listen_host = "127.0.0.1";
974 int listen_port = 3130;
975 if (!parse_args(argc, argv, listen_host, listen_port)) return 2;
976
977 CefSettings settings;
978
979 // ANTI-CRASH IN SANDBOX: il sandbox interno di Chromium non può
980 // annidarsi dentro un altro confinamento. Se siamo confinati, lo
981 // disabilitiamo per evitare l'abort della zygote (vedi running_confined()).
982 // La decisione è preventiva: CefInitialize non si può ri-eseguire nello
983 // stesso processo, quindi NON tentiamo un retry in-process (inefficace);
984 // ci affidiamo alla rilevazione a monte, come fa SecureBrowser.
985 const bool confined = running_confined();
986 settings.no_sandbox = confined;
987 settings.multi_threaded_message_loop = true;
988 settings.windowless_rendering_enabled = false;
989
990 if (confined) {
991 std::cerr << _("[cef] ambiente confinato rilevato → sandbox CEF disabilitato\n");
992 }
993
994 if (!CefInitialize(main_args, settings, app, nullptr)) {
995 std::cerr << _("[cef] CefInitialize failed\n");
996 return 1;
997 }
998
999 std::cerr << (settings.no_sandbox
1000 ? _("[cef] initialized (sandbox OFF)\n")
1001 : _("[cef] initialized (sandbox ON)\n"));
1003
1004 domain_filter().load();
1005 LocalWarningServer warning_server(
1009 warning_server.start();
1010
1011 ProxyConfig::apply({ listen_host, listen_port });
1012
1013 struct Guard {
1014 LocalWarningServer& ws;
1015 ~Guard() {
1017 ws.stop();
1018 }
1019 } guard{warning_server};
1020
1021 std::vector<std::thread> io_threads;
1022 io_threads.reserve(IO_THREADS);
1023
1024 {
1025 char buf[128];
1026 std::snprintf(buf, sizeof(buf), _("[cef] starting %u I/O threads\n"), IO_THREADS);
1027 std::cerr << buf;
1028 }
1029
1030 asio::signal_set sig(g_io, SIGINT, SIGTERM);
1031 sig.async_wait([&](const boost::system::error_code&, int) {
1032 std::cerr << _("[cef] signal received, exiting\n");
1034 guard.ws.stop();
1035 g_io.stop();
1036 });
1037
1038 asio::signal_set sighup(g_io, SIGHUP);
1039 std::function<void()> arm_sighup = [&]() {
1040 sighup.async_wait([&](const boost::system::error_code& ec, int) {
1041 if (ec) return;
1042 std::cerr << _("[filter] SIGHUP: reloading domain lists\n");
1043 domain_filter().load();
1044 arm_sighup();
1045 });
1046 };
1047 arm_sighup();
1048
1049 tcp::acceptor acceptor(g_io);
1050 {
1051 boost::system::error_code ec;
1052 tcp::endpoint ep(asio::ip::make_address(listen_host),
1053 static_cast<unsigned short>(listen_port));
1054
1055 acceptor.open(ep.protocol(), ec);
1056 if (!ec) acceptor.set_option(asio::socket_base::reuse_address(true), ec);
1057 if (!ec) acceptor.bind(ep, ec);
1058 if (!ec) acceptor.listen(asio::socket_base::max_listen_connections, ec);
1059
1060 if (ec) {
1061 char buf[256];
1062 std::snprintf(buf, sizeof(buf), _("[cef] acceptor error: %s\n"), ec.message().c_str());
1063 std::cerr << buf;
1065 warning_server.stop();
1066 CefShutdown();
1067 return 1;
1068 }
1069 }
1070
1071 {
1072 char buf[256];
1073 std::snprintf(buf, sizeof(buf), _("[cef] backend proxy listening on %s:%d\n"),
1074 listen_host.c_str(), listen_port);
1075 std::cerr << buf;
1076 }
1077
1078 do_accept(acceptor, argc, argv);
1079
1080 for (unsigned int i = 1; i < IO_THREADS; ++i)
1081 io_threads.emplace_back([]() { g_io.run(); });
1082
1083 g_io.run();
1084
1085 for (auto& t : io_threads)
1086 if (t.joinable()) t.join();
1087
1088 CefShutdown();
1089 return 0;
1090}
static bool running_confined()
static bool parse_args(int argc, char **argv, std::string &listen_host, int &listen_port)
#define BG_WARNING_HTTPS_PORT
#define LOCALEDIR
static const unsigned int IO_THREADS
static void apply_language()
static bool ensure_trusted()
static void apply(const Settings &s)
static void restore()

References _, SettingsStore::apply_language(), BastionApp::BastionApp(), BG_WARNING_ADDR, BG_WARNING_HTTP_PORT, BG_WARNING_HTTPS_PORT, BastionGuard::Platform::cef_available(), do_accept(), domain_filter(), g_io, IO_THREADS, DomainFilter::load(), LOCALEDIR, main(), parse_args(), running_confined(), LocalWarningServer::start(), and LocalWarningServer::stop().

◆ open_notify_window()

void open_notify_window ( const std::string & root,
const std::string & host,
const std::string & type,
int argc,
char ** argv )
static

Definition at line 477 of file bastionguard_cef.cpp.

482{
483 {
484 std::lock_guard<std::mutex> lock(g_notify_mutex);
485 if (g_notify_open.count(root)) return;
486 g_notify_open.insert(root);
487 }
488
489 const std::string url = warning_url_for(host, type);
490
491 std::thread([root, url, argc, argv]() {
492 try {
493 SecureBrowser::open_notify(url, argc, argv);
494 } catch (const std::exception& e) {
495 char buf[512];
496 std::snprintf(buf, sizeof(buf),
497 _("[bastionguard] open_notify exception: %s\n"), e.what());
498 std::cerr << buf;
499 } catch (...) {
500 std::cerr << _("[bastionguard] open_notify unknown exception\n");
501 }
502 std::lock_guard<std::mutex> lock(g_notify_mutex);
503 g_notify_open.erase(root);
504 }).detach();
505}
static std::unordered_set< std::string > g_notify_open
static std::mutex g_notify_mutex
static void open_notify(const std::string &url, int argc, char **argv)

References _, g_notify_mutex, g_notify_open, SecureBrowser::open_notify(), open_notify_window(), and warning_url_for().

Referenced by block_secure_request(), and open_notify_window().

◆ parse_args()

bool parse_args ( int argc,
char ** argv,
std::string & listen_host,
int & listen_port )
static

Definition at line 843 of file bastionguard_cef.cpp.

843 {
844 for (int i = 1; i < argc; ++i) {
845 const std::string_view a = argv[i];
846
847 if (a.starts_with("--type=") || a.starts_with("--lang=") ||
848 a.starts_with("--user-data-dir=") || a.starts_with("--enable-") ||
849 a.starts_with("--disable-") || a.starts_with("--service-") ||
850 a.starts_with("--utility-") || a.starts_with("--ozone-") ||
851 a.starts_with("--render-node-") || a.starts_with("--change-stack-") ||
852 a.starts_with("--no-zygote"))
853 continue;
854
855 if (a == "--listen" && i + 1 < argc) {
856 listen_host = argv[++i];
857 } else if (a == "--port" && i + 1 < argc) {
858 try {
859 const int p = std::stoi(argv[++i]);
860 if (p < 1 || p > 65535) throw std::out_of_range("port");
861 listen_port = p;
862 } catch (...) {
863 std::cerr << _("[args] invalid --port value\n");
864 return false;
865 }
866 }
867 }
868
869 return true;
870}

References _, and parse_args().

Referenced by main(), main(), parse_args(), and parse_args().

◆ parse_request()

bool parse_request ( const std::string & first_line,
ParsedRequest & out )
static

Definition at line 309 of file bastionguard_cef.cpp.

309 {
310 std::istringstream ss(first_line);
311 std::string method, target, ver;
312 ss >> method >> target >> ver;
313 if (method.empty() || target.empty()) return false;
314
315 out.method = method;
316
317 if (method == "CONNECT") {
318 const auto p = target.find(':');
319 if (p == std::string::npos) return false;
320 out.host = target.substr(0, p);
321 out.port = target.substr(p + 1);
322 out.is_connect = true;
323 return !out.host.empty() && !out.port.empty();
324 }
325
326 if (target.size() > 7 && target.substr(0, 7) == "http://") {
327 std::string rest = target.substr(7);
328 const auto slash = rest.find('/');
329 const std::string authority = slash != std::string::npos ? rest.substr(0, slash) : rest;
330 out.path = slash != std::string::npos ? rest.substr(slash) : "/";
331
332 const auto colon = authority.rfind(':');
333 if (colon != std::string::npos) {
334 out.host = authority.substr(0, colon);
335 out.port = authority.substr(colon + 1);
336 } else {
337 out.host = authority;
338 out.port = "80";
339 }
340
341 out.is_http_plain = true;
342 return !out.host.empty();
343 }
344
345 return false;
346}

References ParsedRequest::host, ParsedRequest::is_connect, ParsedRequest::is_http_plain, ParsedRequest::method, parse_request(), ParsedRequest::path, and ParsedRequest::port.

Referenced by handle_client(), and parse_request().

◆ root_domain()

std::string root_domain ( const std::string & host)
static

Definition at line 90 of file bastionguard_cef.cpp.

90 {
91 const auto dots = std::count(host.begin(), host.end(), '.');
92 if (dots <= 1) return host;
93 const auto p1 = host.rfind('.');
94 const auto p2 = host.rfind('.', p1 - 1);
95 return host.substr(p2 + 1);
96}

References root_domain().

Referenced by handle_client(), and root_domain().

◆ running_confined()

bool running_confined ( )
static

Definition at line 887 of file bastionguard_cef.cpp.

887 {
888 // 1) Flag esplicito impostato da launch_sandbox() / dal wrapper.
889 if (std::getenv("BG_SANDBOX")) return true;
890 if (std::getenv("BG_NO_CEF_SANDBOX")) return true;
891
892 // 2) Flatpak / Snap.
893 if (std::getenv("FLATPAK_ID")) return true;
894 if (std::getenv("SNAP")) return true;
895 if (access("/.flatpak-info", F_OK) == 0) return true;
896
897 // 3) Container generico (Docker/Podman/systemd-nspawn ecc.).
898 if (access("/run/.containerenv", F_OK) == 0) return true;
899 {
900 std::ifstream cg("/proc/1/cgroup");
901 std::string line;
902 while (std::getline(cg, line)) {
903 if (line.find("docker") != std::string::npos ||
904 line.find("libpod") != std::string::npos ||
905 line.find("/lxc") != std::string::npos) {
906 return true;
907 }
908 }
909 }
910
911 // 4) Namespace utente non privilegiati disabilitati nel kernel:
912 // senza di essi il sandbox di Chromium non parte (a meno del SUID
913 // helper, che in molte installazioni/portali non è disponibile).
914 {
915 std::ifstream f("/proc/sys/kernel/unprivileged_userns_clone");
916 if (f.good()) {
917 int v = 1;
918 f >> v;
919 if (v == 0) return true;
920 }
921 }
922
923 // 5) Il SUID sandbox helper di CEF non è presente/eseguibile: senza di
924 // esso e senza userns, il sandbox non può attivarsi.
925 // (Percorsi tipici di installazione CEF di BastionGuard.)
926 {
927 static const char* helpers[] = {
928 "/usr/share/BastionGuard/cef/chrome-sandbox",
929 "/usr/lib/BastionGuard/chrome-sandbox",
930 };
931 bool any_helper = false;
932 for (const char* h : helpers) {
933 struct stat st{};
934 if (stat(h, &st) == 0) {
935 any_helper = true;
936 // Deve essere SUID-root per funzionare.
937 if ((st.st_mode & S_ISUID) && st.st_uid == 0) {
938 any_helper = true;
939 }
940 }
941 }
942 // Se nessun helper SUID valido e nessun userns, meglio disattivare.
943 if (!any_helper) {
944 std::ifstream f("/proc/sys/kernel/unprivileged_userns_clone");
945 int v = 1;
946 if (f.good()) f >> v;
947 if (v == 0) return true;
948 }
949 }
950
951 return false;
952}
#define S_ISUID

References running_confined(), and S_ISUID.

Referenced by BastionApp::BastionApp(), main(), and running_confined().

◆ sandbox_bind_pid()

void sandbox_bind_pid ( const std::string & root,
pid_t pid )
static

Definition at line 520 of file bastionguard_cef.cpp.

520 {
521 std::lock_guard<std::mutex> lock(g_sandbox_mutex);
522 if (g_sandbox_open.count(root))
523 g_sandbox_pid[root] = pid;
524}
static std::mutex g_sandbox_mutex
static std::unordered_map< std::string, pid_t > g_sandbox_pid
static std::unordered_set< std::string > g_sandbox_open

References g_sandbox_mutex, g_sandbox_open, g_sandbox_pid, and sandbox_bind_pid().

Referenced by launch_sandbox(), and sandbox_bind_pid().

◆ sandbox_close()

void sandbox_close ( const std::string & root)
static

Definition at line 535 of file bastionguard_cef.cpp.

535 {
536 std::lock_guard<std::mutex> lock(g_sandbox_mutex);
537 g_sandbox_pid.erase(root);
538 g_sandbox_open.erase(root);
539}

References g_sandbox_mutex, g_sandbox_open, g_sandbox_pid, and sandbox_close().

Referenced by launch_sandbox(), sandbox_close(), and session_active().

◆ sandbox_close_if_pid()

void sandbox_close_if_pid ( const std::string & root,
pid_t pid )
static

Definition at line 526 of file bastionguard_cef.cpp.

526 {
527 std::lock_guard<std::mutex> lock(g_sandbox_mutex);
528 auto it = g_sandbox_pid.find(root);
529 if (it != g_sandbox_pid.end() && it->second == pid) {
530 g_sandbox_pid.erase(it);
531 g_sandbox_open.erase(root);
532 }
533}

References g_sandbox_mutex, g_sandbox_open, g_sandbox_pid, and sandbox_close_if_pid().

Referenced by launch_sandbox(), and sandbox_close_if_pid().

◆ sandbox_try_open()

bool sandbox_try_open ( const std::string & root)
static

Definition at line 512 of file bastionguard_cef.cpp.

512 {
513 std::lock_guard<std::mutex> lock(g_sandbox_mutex);
514 const bool inserted = g_sandbox_open.insert(root).second;
515 if (inserted)
516 g_sandbox_pid[root] = -1;
517 return inserted;
518}

References g_sandbox_mutex, g_sandbox_open, g_sandbox_pid, and sandbox_try_open().

Referenced by block_secure_request(), and sandbox_try_open().

◆ session_active()

bool session_active ( const std::string & root,
std::string & out_type )
static

Definition at line 115 of file bastionguard_cef.cpp.

115 {
116 bool expired = false;
117 {
118 std::lock_guard<std::mutex> lock(g_session_mutex);
119 auto it = g_sessions.find(root);
120 if (it == g_sessions.end()) return false;
121 if (Clock::now() - it->second.opened_at > SESSION_TTL) {
122 g_sessions.erase(it);
123 expired = true;
124 } else {
125 out_type = it->second.type;
126 return true;
127 }
128 }
129 // Sessione scaduta: libera anche il gate sandbox (fuori dal lock di
130 // sessione per evitare di tenere due mutex insieme).
131 if (expired) sandbox_close(root);
132 return false;
133}
static std::unordered_map< std::string, SessionEntry > g_sessions
static std::mutex g_session_mutex
static constexpr auto SESSION_TTL

References g_session_mutex, g_sessions, sandbox_close(), session_active(), and SESSION_TTL.

Referenced by handle_client(), and session_active().

◆ session_bind_pid()

void session_bind_pid ( const std::string & root,
pid_t pid )
static

Definition at line 144 of file bastionguard_cef.cpp.

144 {
145 std::lock_guard<std::mutex> lock(g_session_mutex);
146 auto it = g_sessions.find(root);
147 if (it != g_sessions.end())
148 it->second.child_pid = pid;
149}

References g_session_mutex, g_sessions, and session_bind_pid().

Referenced by launch_sandbox(), and session_bind_pid().

◆ session_close()

void session_close ( const std::string & root)
static

Definition at line 151 of file bastionguard_cef.cpp.

151 {
152 bool removed = false;
153 {
154 std::lock_guard<std::mutex> lock(g_session_mutex);
155 removed = g_sessions.erase(root) != 0;
156 }
157 if (removed) {
158 char msg[256];
159 std::snprintf(msg, sizeof(msg),
160 _("[bastionguard] session closed for root: %s — browser proxy restored\n"),
161 root.c_str());
162 std::cerr << msg;
163 }
164}

References _, g_session_mutex, g_sessions, and session_close().

Referenced by block_secure_request(), launch_sandbox(), and session_close().

◆ session_close_if_pid()

void session_close_if_pid ( const std::string & root,
pid_t pid )
static

Definition at line 166 of file bastionguard_cef.cpp.

166 {
167 bool removed = false;
168 {
169 std::lock_guard<std::mutex> lock(g_session_mutex);
170 auto it = g_sessions.find(root);
171 if (it != g_sessions.end() && it->second.child_pid == pid) {
172 g_sessions.erase(it);
173 removed = true;
174 }
175 }
176 if (removed) {
177 char msg[320];
178 std::snprintf(msg, sizeof(msg),
179 _("[bastionguard] secure browser pid %ld closed for root: %s — browser proxy restored\n"),
180 static_cast<long>(pid), root.c_str());
181 std::cerr << msg;
182 }
183}

References _, g_session_mutex, g_sessions, and session_close_if_pid().

Referenced by launch_sandbox(), and session_close_if_pid().

◆ session_try_open()

bool session_try_open ( const std::string & root,
const std::string & type )
static

Definition at line 137 of file bastionguard_cef.cpp.

137 {
138 std::lock_guard<std::mutex> lock(g_session_mutex);
139 auto [it, inserted] = g_sessions.emplace(
140 root, SessionEntry{Clock::now(), type, -1});
141 return inserted;
142}

References g_session_mutex, g_sessions, and session_try_open().

Referenced by block_secure_request(), and session_try_open().

◆ warning_url_for()

std::string warning_url_for ( const std::string & host,
const std::string & type )
static

Definition at line 463 of file bastionguard_cef.cpp.

463 {
464 return "https://bastionguard.eu/secure.html?host=" + host + "&type=" + type;
465}

References warning_url_for().

Referenced by block_active_session(), block_secure_request(), open_notify_window(), and warning_url_for().

Variable Documentation

◆ g_io

asio::io_context g_io {static_cast<int>(IO_THREADS)}
static

Definition at line 88 of file bastionguard_cef.cpp.

Referenced by async_forward_http(), async_tunnel(), do_accept(), and main().

◆ g_notify_mutex

std::mutex g_notify_mutex
static

Definition at line 474 of file bastionguard_cef.cpp.

Referenced by open_notify_window().

◆ g_notify_open

std::unordered_set<std::string> g_notify_open
static

Definition at line 475 of file bastionguard_cef.cpp.

Referenced by open_notify_window().

◆ g_sandbox_mutex

std::mutex g_sandbox_mutex
static

◆ g_sandbox_open

std::unordered_set<std::string> g_sandbox_open
static

◆ g_sandbox_pid

std::unordered_map<std::string, pid_t> g_sandbox_pid
static

◆ g_session_mutex

std::mutex g_session_mutex
static

◆ g_sessions

std::unordered_map<std::string, SessionEntry> g_sessions
static

◆ IO_THREADS

const unsigned int IO_THREADS
static
Initial value:
=
std::max(2u, std::thread::hardware_concurrency())

Definition at line 81 of file bastionguard_cef.cpp.

Referenced by main().

◆ SESSION_TTL

auto SESSION_TTL = std::chrono::minutes(30)
staticconstexpr

Definition at line 79 of file bastionguard_cef.cpp.

Referenced by session_active().