From ed941ad16f4504380d081476a2543c13222e1f72 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Thu, 2 Jul 2026 22:33:00 +0900 Subject: [PATCH] FreeBSD: support opendoas elevation Reuse the OpenBSD doas PTY prompt flow on FreeBSD so opendoas receives the password through its controlling terminal. Apply the same foreground process group validation on FreeBSD as on OpenBSD when attaching the private doas authentication PTY. Detect the incompatible FreeBSD security/doas package by pkg origin and fail with explicit guidance. --- src/Core/Unix/CoreService.cpp | 66 ++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/src/Core/Unix/CoreService.cpp b/src/Core/Unix/CoreService.cpp index c95e51ac..fd7bc577 100644 --- a/src/Core/Unix/CoreService.cpp +++ b/src/Core/Unix/CoreService.cpp @@ -147,6 +147,48 @@ namespace VeraCrypt close (f); } +#ifdef TC_FREEBSD + static string TrimCommandOutput (const string &output) + { + size_t first = output.find_first_not_of (" \t\r\n"); + if (first == string::npos) + return string(); + + size_t last = output.find_last_not_of (" \t\r\n"); + return output.substr (first, last - first + 1); + } + + static bool IsFreeBSDSecurityDoasPackage (const string &doasPath) + { + try + { + std::string errorMsg; + string pkgPath = Process::FindSystemBinary ("pkg", errorMsg); + if (pkgPath.empty()) + return false; + + list args; + args.push_back ("which"); + args.push_back ("-q"); + args.push_back (doasPath); + string packageName = TrimCommandOutput (Process::Execute (pkgPath, args, 5000)); + if (packageName.empty()) + return false; + + args.clear(); + args.push_back ("info"); + args.push_back ("-q"); + args.push_back ("-o"); + args.push_back (packageName); + string packageOrigin = TrimCommandOutput (Process::Execute (pkgPath, args, 5000)); + return packageOrigin == "security/doas"; + } + catch (...) { } + + return false; + } +#endif + static PrivilegeHelper FindPrivilegeHelper () { std::string errorMsg; @@ -156,7 +198,13 @@ namespace VeraCrypt path = Process::FindSystemBinary ("doas", errorMsg); if (!path.empty()) + { +#ifdef TC_FREEBSD + if (IsFreeBSDSecurityDoasPackage (path)) + throw ElevationFailed (SRC_POS, "doas", 1, "The FreeBSD security/doas package is incompatible with VeraCrypt elevation. Install security/opendoas or sudo."); +#endif return { PrivilegeHelperType::Doas, "doas", path }; + } throw SystemException (SRC_POS, "Neither sudo nor doas was found in system directories"); } @@ -304,7 +352,7 @@ namespace VeraCrypt throw SystemException (SRC_POS, "Failed to set doas authentication terminal as controlling terminal"); } #endif -#ifdef TC_OPENBSD +#if defined (TC_OPENBSD) || defined (TC_FREEBSD) if (tcsetpgrp (ttyFd, getpgrp()) == -1) { int err = errno; @@ -569,7 +617,7 @@ namespace VeraCrypt } } -#ifdef TC_OPENBSD +#if defined (TC_OPENBSD) || defined (TC_FREEBSD) static bool ReadDoasAuthTerminalPromptData (int fd, vector &authOutput) { char buffer[256]; @@ -1389,11 +1437,11 @@ namespace VeraCrypt } #endif bool useDoasAuthTerminal = privilegeHelper.IsDoas() && !useCallerDoasTerminal; -#ifdef TC_OPENBSD - // OpenBSD doas requires stderr to be a terminal while it - // prompts for the password. Keeping the private PTY slave - // on stderr satisfies that while stdin/stdout stay as the - // service pipes. +#if defined (TC_OPENBSD) || defined (TC_FREEBSD) + // OpenBSD doas and FreeBSD opendoas require stderr to be + // a terminal while prompting for the password. Keeping the + // private PTY slave on stderr satisfies that while stdin/stdout + // stay as the service pipes. bool keepDoasStderrOnAuthTerminal = useDoasAuthTerminal; #else bool keepDoasStderrOnAuthTerminal = false; @@ -1486,7 +1534,7 @@ namespace VeraCrypt { // doas reads authentication from the controlling terminal, not stdin. bool writeDoasPassword = true; -#ifdef TC_OPENBSD +#if defined (TC_OPENBSD) || defined (TC_FREEBSD) writeDoasPassword = WaitForDoasAuthTerminalPrompt (doasAuthTerminal, authOutput, timeout); #endif if (writeDoasPassword) @@ -1504,7 +1552,7 @@ namespace VeraCrypt shared_ptr outputStream (new FileStream (serviceOutputFd)); int authOutputFd = -1; -#ifdef TC_OPENBSD +#if defined (TC_OPENBSD) || defined (TC_FREEBSD) if (doasAuthTerminal != -1) { int authTerminalFlags = fcntl (doasAuthTerminal, F_GETFL, 0);