mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-06-09 21:22:41 +00:00
Unix: harden doas startup fd handling
Move child pipe descriptors away from stdio slots before remapping them, avoiding collisions when 0/1/2 are closed. On OpenBSD, wait for authentication-terminal output before writing the doas password, avoiding prompt text matching while keeping the startup timeout as the upper bound.
This commit is contained in:
@@ -73,6 +73,57 @@ namespace VeraCrypt
|
||||
SetCloseOnExec (pipe.PeekWriteFD(), true);
|
||||
}
|
||||
|
||||
static bool IsStandardFd (int fd)
|
||||
{
|
||||
return fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO;
|
||||
}
|
||||
|
||||
static int DuplicateCloseOnExec (int fd)
|
||||
{
|
||||
int duplicateFd;
|
||||
#ifdef F_DUPFD_CLOEXEC
|
||||
duplicateFd = fcntl (fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
|
||||
if (duplicateFd != -1)
|
||||
return duplicateFd;
|
||||
|
||||
if (errno != EINVAL)
|
||||
throw SystemException (SRC_POS);
|
||||
#endif
|
||||
duplicateFd = fcntl (fd, F_DUPFD, STDERR_FILENO + 1);
|
||||
throw_sys_if (duplicateFd == -1);
|
||||
|
||||
try
|
||||
{
|
||||
SetCloseOnExec (duplicateFd, true);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
close (duplicateFd);
|
||||
throw;
|
||||
}
|
||||
|
||||
return duplicateFd;
|
||||
}
|
||||
|
||||
static void MoveFdAwayFromStandardFdsIfNeeded (int &fd)
|
||||
{
|
||||
if (fd != -1 && IsStandardFd (fd))
|
||||
fd = DuplicateCloseOnExec (fd);
|
||||
}
|
||||
|
||||
static void MoveFdAwayFromStandardFdIfNeeded (int &fd, int standardFd)
|
||||
{
|
||||
if (fd != standardFd)
|
||||
MoveFdAwayFromStandardFdsIfNeeded (fd);
|
||||
}
|
||||
|
||||
static void PrepareStandardFdMapping (int &stdinFd, int &stdoutFd, int &stderrFd)
|
||||
{
|
||||
MoveFdAwayFromStandardFdIfNeeded (stdinFd, STDIN_FILENO);
|
||||
MoveFdAwayFromStandardFdIfNeeded (stdoutFd, STDOUT_FILENO);
|
||||
MoveFdAwayFromStandardFdIfNeeded (stderrFd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
static void DupToStandardFd (int fd, int standardFd)
|
||||
{
|
||||
if (fd != standardFd)
|
||||
@@ -544,17 +595,17 @@ namespace VeraCrypt
|
||||
}
|
||||
}
|
||||
|
||||
static void WaitForDoasAuthTerminalPrompt (int fd, vector <char> &authOutput, int timeout)
|
||||
static bool WaitForDoasAuthTerminalPrompt (int fd, vector <char> &authOutput, int timeout)
|
||||
{
|
||||
const int pollInterval = 50;
|
||||
int timeLeft = timeout;
|
||||
int terminalFlags = fcntl (fd, F_GETFL, 0);
|
||||
if (terminalFlags == -1)
|
||||
return;
|
||||
return false;
|
||||
|
||||
bool restoreFlags = (terminalFlags & O_NONBLOCK) == 0;
|
||||
if (restoreFlags && fcntl (fd, F_SETFL, terminalFlags | O_NONBLOCK) == -1)
|
||||
return;
|
||||
return false;
|
||||
|
||||
while (timeLeft > 0)
|
||||
{
|
||||
@@ -580,7 +631,11 @@ namespace VeraCrypt
|
||||
}
|
||||
|
||||
if ((pfd.revents & POLLIN) && ReadDoasAuthTerminalPromptData (fd, authOutput))
|
||||
break;
|
||||
{
|
||||
if (restoreFlags)
|
||||
fcntl (fd, F_SETFL, terminalFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pfd.revents & (POLLERR | POLLNVAL))
|
||||
break;
|
||||
@@ -594,6 +649,8 @@ namespace VeraCrypt
|
||||
|
||||
if (restoreFlags)
|
||||
fcntl (fd, F_SETFL, terminalFlags);
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1304,6 +1361,7 @@ namespace VeraCrypt
|
||||
|
||||
if (forkedPid == 0)
|
||||
{
|
||||
int childErrorFd = -1;
|
||||
try
|
||||
{
|
||||
try
|
||||
@@ -1319,16 +1377,16 @@ namespace VeraCrypt
|
||||
}
|
||||
|
||||
#if defined(TC_LINUX)
|
||||
// AppImage specific handling:
|
||||
// If running from an AppImage, use the path to the AppImage file itself for the privilege helper.
|
||||
const char* appImageEnv = getenv("APPIMAGE");
|
||||
// AppImage specific handling:
|
||||
// If running from an AppImage, use the path to the AppImage file itself for the privilege helper.
|
||||
const char* appImageEnv = getenv("APPIMAGE");
|
||||
|
||||
if (Process::IsRunningUnderAppImage(appPath) && appImageEnv != NULL)
|
||||
{
|
||||
// The path to the AppImage file is stored in the APPIMAGE environment variable.
|
||||
// We need to use this path for elevation to work correctly.
|
||||
appPath = appImageEnv;
|
||||
}
|
||||
appPath = appImageEnv;
|
||||
}
|
||||
#endif
|
||||
bool useDoasAuthTerminal = privilegeHelper.IsDoas() && !useCallerDoasTerminal;
|
||||
#ifdef TC_OPENBSD
|
||||
@@ -1340,6 +1398,16 @@ namespace VeraCrypt
|
||||
#else
|
||||
bool keepDoasStderrOnAuthTerminal = false;
|
||||
#endif
|
||||
int childStdinFd = inPipe->GetReadFD();
|
||||
int childStdoutFd = outPipe->GetWriteFD();
|
||||
childErrorFd = errPipe.GetWriteFD();
|
||||
int childStderrFd = keepDoasStderrOnAuthTerminal ? -1 : childErrorFd;
|
||||
PrepareStandardFdMapping (childStdinFd, childStdoutFd, childStderrFd);
|
||||
if (keepDoasStderrOnAuthTerminal)
|
||||
MoveFdAwayFromStandardFdsIfNeeded (childErrorFd);
|
||||
else
|
||||
childErrorFd = childStderrFd;
|
||||
|
||||
if (useDoasAuthTerminal)
|
||||
{
|
||||
AttachDoasAuthTerminal (doasAuthTerminalPath, keepDoasStderrOnAuthTerminal);
|
||||
@@ -1349,9 +1417,9 @@ namespace VeraCrypt
|
||||
close (doasAuthTerminal);
|
||||
|
||||
if (!keepDoasStderrOnAuthTerminal)
|
||||
DupToStandardFd (errPipe.GetWriteFD(), STDERR_FILENO);
|
||||
DupToStandardFd (inPipe->GetReadFD(), STDIN_FILENO);
|
||||
DupToStandardFd (outPipe->GetWriteFD(), STDOUT_FILENO);
|
||||
DupToStandardFd (childStderrFd, STDERR_FILENO);
|
||||
DupToStandardFd (childStdinFd, STDIN_FILENO);
|
||||
DupToStandardFd (childStdoutFd, STDOUT_FILENO);
|
||||
|
||||
const char *sudoArgs[] = { privilegeHelper.Path.c_str(), "-S", "-p", "", appPath.c_str(), TC_CORE_SERVICE_CMDLINE_OPTION, nullptr };
|
||||
const char *doasArgs[] = { privilegeHelper.Path.c_str(), appPath.c_str(), TC_CORE_SERVICE_NO_FORK_CMDLINE_OPTION, nullptr };
|
||||
@@ -1377,7 +1445,7 @@ namespace VeraCrypt
|
||||
{
|
||||
try
|
||||
{
|
||||
shared_ptr <Stream> outputStream (new FileStream (errPipe.GetWriteFD()));
|
||||
shared_ptr <Stream> outputStream (new FileStream (childErrorFd != -1 ? childErrorFd : errPipe.GetWriteFD()));
|
||||
e.Serialize (outputStream);
|
||||
}
|
||||
catch (...) { }
|
||||
@@ -1417,10 +1485,12 @@ namespace VeraCrypt
|
||||
else if (doasAuthTerminal != -1 && !doasNoPasswordAttempt)
|
||||
{
|
||||
// doas reads authentication from the controlling terminal, not stdin.
|
||||
bool writeDoasPassword = true;
|
||||
#ifdef TC_OPENBSD
|
||||
WaitForDoasAuthTerminalPrompt (doasAuthTerminal, authOutput, 2000);
|
||||
writeDoasPassword = WaitForDoasAuthTerminalPrompt (doasAuthTerminal, authOutput, timeout);
|
||||
#endif
|
||||
WriteAllBestEffort (doasAuthTerminal, &adminPassword.front(), adminPassword.size(), 2000);
|
||||
if (writeDoasPassword)
|
||||
WriteAllBestEffort (doasAuthTerminal, &adminPassword.front(), adminPassword.size(), 2000);
|
||||
}
|
||||
|
||||
burn (&adminPassword.front(), adminPassword.size());
|
||||
|
||||
Reference in New Issue
Block a user