mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-07-05 20:48:00 -05:00
Unix: add doas elevation support
Prefer sudo when available and fall back to doas on Unix. Run doas authentication through a PTY while keeping service communication on stdin/stdout pipes, and use a no-fork service mode for the doas path. Keep doas authentication terminal descriptors close-on-exec and close the slave descriptor after attaching it as the controlling terminal. Preserve startup diagnostics through stderr until service synchronization completes, then redirect no-fork service stderr away from the closed parent pipe. Use noninteractive privilege-helper auth checks for both sudo and doas so cached, nopass, or persisted sessions do not need an unnecessary VeraCrypt password prompt. Keep the PTY password path for doas when authentication is required. Use a shared Unix DOAS_USER helper for FUSE and mount ownership, backed by getpwnam_r and guarded so non-OpenBSD platforms only trust it for VeraCrypt's internal doas no-fork service path. Detach asynchronous child-reaper threads to avoid leaking joinable pthread handles.
This commit is contained in:
+510
-56
@@ -11,9 +11,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CoreService.h"
|
#include "CoreService.h"
|
||||||
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "Platform/FileStream.h"
|
#include "Platform/FileStream.h"
|
||||||
#include "Platform/MemoryStream.h"
|
#include "Platform/MemoryStream.h"
|
||||||
@@ -29,6 +37,395 @@
|
|||||||
|
|
||||||
namespace VeraCrypt
|
namespace VeraCrypt
|
||||||
{
|
{
|
||||||
|
enum class PrivilegeHelperType
|
||||||
|
{
|
||||||
|
Sudo,
|
||||||
|
Doas
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PrivilegeHelper
|
||||||
|
{
|
||||||
|
PrivilegeHelperType Type;
|
||||||
|
string Name;
|
||||||
|
string Path;
|
||||||
|
|
||||||
|
bool IsDoas () const { return Type == PrivilegeHelperType::Doas; }
|
||||||
|
bool IsSudo () const { return Type == PrivilegeHelperType::Sudo; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep the PTY master open while the doas no-fork service is running;
|
||||||
|
// closing it can hang up the service controlling terminal.
|
||||||
|
static int DoasAuthTerminalFd = -1;
|
||||||
|
|
||||||
|
static void RedirectStandardErrorToDevNull ()
|
||||||
|
{
|
||||||
|
int f = open ("/dev/null", O_WRONLY);
|
||||||
|
throw_sys_sub_if (f == -1, "/dev/null");
|
||||||
|
if (dup2 (f, STDERR_FILENO) == -1)
|
||||||
|
{
|
||||||
|
close (f);
|
||||||
|
throw SystemException (SRC_POS);
|
||||||
|
}
|
||||||
|
if (f != STDERR_FILENO)
|
||||||
|
close (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PrivilegeHelper FindPrivilegeHelper ()
|
||||||
|
{
|
||||||
|
std::string errorMsg;
|
||||||
|
string path = Process::FindSystemBinary ("sudo", errorMsg);
|
||||||
|
if (!path.empty())
|
||||||
|
return { PrivilegeHelperType::Sudo, "sudo", path };
|
||||||
|
|
||||||
|
path = Process::FindSystemBinary ("doas", errorMsg);
|
||||||
|
if (!path.empty())
|
||||||
|
return { PrivilegeHelperType::Doas, "doas", path };
|
||||||
|
|
||||||
|
throw SystemException (SRC_POS, "Neither sudo nor doas was found in system directories");
|
||||||
|
}
|
||||||
|
|
||||||
|
static string BuildPrivilegeHelperAuthCheckCommand (const PrivilegeHelper &helper)
|
||||||
|
{
|
||||||
|
std::string errorMsg;
|
||||||
|
string trueAbsolutePath = Process::FindSystemBinary ("true", errorMsg);
|
||||||
|
if (trueAbsolutePath.empty())
|
||||||
|
throw SystemException (SRC_POS, errorMsg);
|
||||||
|
|
||||||
|
return helper.Path + " -n " + trueAbsolutePath + " > /dev/null 2>&1";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasControllingTerminal ()
|
||||||
|
{
|
||||||
|
#ifdef O_CLOEXEC
|
||||||
|
int fd = open ("/dev/tty", O_RDWR | O_CLOEXEC);
|
||||||
|
#else
|
||||||
|
int fd = open ("/dev/tty", O_RDWR);
|
||||||
|
#endif
|
||||||
|
if (fd == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifndef O_CLOEXEC
|
||||||
|
if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
|
{
|
||||||
|
close (fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
close (fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int OpenDoasAuthTerminal (string &slavePath)
|
||||||
|
{
|
||||||
|
#ifdef O_CLOEXEC
|
||||||
|
int fd = posix_openpt (O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||||
|
#else
|
||||||
|
int fd = posix_openpt (O_RDWR | O_NOCTTY);
|
||||||
|
#endif
|
||||||
|
throw_sys_sub_if (fd == -1, "posix_openpt");
|
||||||
|
|
||||||
|
#ifndef O_CLOEXEC
|
||||||
|
if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
|
{
|
||||||
|
close (fd);
|
||||||
|
throw SystemException (SRC_POS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (grantpt (fd) == -1 || unlockpt (fd) == -1)
|
||||||
|
{
|
||||||
|
close (fd);
|
||||||
|
throw SystemException (SRC_POS, "Failed to initialize doas authentication terminal");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (TC_LINUX)
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int ptsStatus = ptsname_r (fd, path, sizeof (path));
|
||||||
|
if (ptsStatus != 0)
|
||||||
|
{
|
||||||
|
close (fd);
|
||||||
|
throw SystemException (SRC_POS, ptsStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
slavePath = path;
|
||||||
|
#else
|
||||||
|
char *path = ptsname (fd);
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
close (fd);
|
||||||
|
throw SystemException (SRC_POS, "Failed to get doas authentication terminal path");
|
||||||
|
}
|
||||||
|
|
||||||
|
slavePath = path;
|
||||||
|
#endif
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CloseDoasAuthTerminal ()
|
||||||
|
{
|
||||||
|
if (DoasAuthTerminalFd != -1)
|
||||||
|
{
|
||||||
|
close (DoasAuthTerminalFd);
|
||||||
|
DoasAuthTerminalFd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AttachDoasAuthTerminal (const string &slavePath)
|
||||||
|
{
|
||||||
|
throw_sys_if (setsid () == -1);
|
||||||
|
#ifdef O_CLOEXEC
|
||||||
|
int ttyFd = open (slavePath.c_str(), O_RDWR | O_CLOEXEC);
|
||||||
|
#else
|
||||||
|
int ttyFd = open (slavePath.c_str(), O_RDWR);
|
||||||
|
#endif
|
||||||
|
throw_sys_sub_if (ttyFd == -1, slavePath);
|
||||||
|
|
||||||
|
#ifndef O_CLOEXEC
|
||||||
|
if (fcntl (ttyFd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
|
{
|
||||||
|
close (ttyFd);
|
||||||
|
throw SystemException (SRC_POS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// doas reads the passphrase from this terminal with the slave line
|
||||||
|
// discipline active. Put it in raw mode so control characters in the
|
||||||
|
// admin password (^C, ^U, erase, etc.) reach doas verbatim instead of
|
||||||
|
// being interpreted as line editing or signal keys. This only ever
|
||||||
|
// touches VeraCrypt's private authentication PTY, never the caller's
|
||||||
|
// real terminal. Best effort: on failure we keep the default canonical
|
||||||
|
// mode, which still handles ordinary passwords.
|
||||||
|
struct termios tios;
|
||||||
|
if (tcgetattr (ttyFd, &tios) == 0)
|
||||||
|
{
|
||||||
|
tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN);
|
||||||
|
tios.c_iflag &= ~(BRKINT | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||||
|
tios.c_cc[VMIN] = 1;
|
||||||
|
tios.c_cc[VTIME] = 0;
|
||||||
|
tcsetattr (ttyFd, TCSANOW, &tios);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TIOCSCTTY
|
||||||
|
if (ioctl (ttyFd, TIOCSCTTY, 0) == -1 && errno != EINVAL)
|
||||||
|
{
|
||||||
|
close (ttyFd);
|
||||||
|
throw SystemException (SRC_POS, "Failed to set doas authentication terminal as controlling terminal");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
close (ttyFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReapChildProcessAsync (int pid)
|
||||||
|
{
|
||||||
|
struct WaitFunctor : public Functor
|
||||||
|
{
|
||||||
|
WaitFunctor (int processId) : Pid (processId) { }
|
||||||
|
virtual void operator() ()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
int waitResult = waitpid (Pid, &status, 0);
|
||||||
|
|
||||||
|
if (waitResult == Pid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (waitResult == -1 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (waitResult == -1 && errno == ECHILD)
|
||||||
|
return;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int Pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unique_ptr <Functor> waitFunctor (new WaitFunctor (pid));
|
||||||
|
Thread thread;
|
||||||
|
thread.Start (waitFunctor.get());
|
||||||
|
waitFunctor.release();
|
||||||
|
thread.Detach ();
|
||||||
|
}
|
||||||
|
catch (...) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TerminateChildProcessAsync (int pid)
|
||||||
|
{
|
||||||
|
if (pid <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
kill (pid, SIGTERM);
|
||||||
|
ReapChildProcessAsync (pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadAvailableData (int fd, vector <char> &output)
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
ssize_t bytesRead = read (fd, buffer, sizeof (buffer));
|
||||||
|
if (bytesRead > 0)
|
||||||
|
{
|
||||||
|
output.insert (output.end(), buffer, buffer + bytesRead);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead == -1 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bytesRead == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||||
|
return;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ErrorOutputToString (const vector <char> &errOutput)
|
||||||
|
{
|
||||||
|
if (errOutput.empty())
|
||||||
|
return string();
|
||||||
|
|
||||||
|
return string (errOutput.begin(), errOutput.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ThrowSerializedExceptionIfAny (const vector <char> &errOutput)
|
||||||
|
{
|
||||||
|
if (errOutput.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
unique_ptr <Serializable> deserializedObject;
|
||||||
|
Exception *deserializedException = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shared_ptr <Stream> stream (new MemoryStream (ConstBufferPtr ((uint8 *) &errOutput[0], errOutput.size())));
|
||||||
|
deserializedObject.reset (Serializable::DeserializeNew (stream));
|
||||||
|
deserializedException = dynamic_cast <Exception*> (deserializedObject.get());
|
||||||
|
}
|
||||||
|
catch (...) { }
|
||||||
|
|
||||||
|
if (deserializedException)
|
||||||
|
deserializedException->Throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteAllBestEffort (int fd, const char *data, size_t size)
|
||||||
|
{
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < size)
|
||||||
|
{
|
||||||
|
ssize_t bytesWritten = write (fd, data + offset, size - offset);
|
||||||
|
if (bytesWritten > 0)
|
||||||
|
{
|
||||||
|
offset += static_cast <size_t> (bytesWritten);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesWritten == -1 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SendElevatedServiceSyncWithTimeout (shared_ptr <Stream> inputStream, int outputFd, int errorFd, int childPid, const string &helperName, int timeout)
|
||||||
|
{
|
||||||
|
vector <char> errOutput;
|
||||||
|
uint8 sync[] = { 0, 0x11, 0x22 };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
inputStream->Write (ConstBufferPtr (sync, array_capacity (sync)));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
ReadAvailableData (errorFd, errOutput);
|
||||||
|
TerminateChildProcessAsync (childPid);
|
||||||
|
ThrowSerializedExceptionIfAny (errOutput);
|
||||||
|
throw ElevationFailed (SRC_POS, helperName, 1, ErrorOutputToString (errOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
const int pollInterval = 200;
|
||||||
|
int timeLeft = timeout;
|
||||||
|
while (timeLeft > 0)
|
||||||
|
{
|
||||||
|
struct pollfd fds[2];
|
||||||
|
memset (fds, 0, sizeof (fds));
|
||||||
|
fds[0].fd = outputFd;
|
||||||
|
fds[0].events = POLLIN;
|
||||||
|
fds[1].fd = errorFd;
|
||||||
|
fds[1].events = POLLIN;
|
||||||
|
|
||||||
|
int pollTimeout = timeLeft < pollInterval ? timeLeft : pollInterval;
|
||||||
|
int pollResult;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pollResult = poll (fds, array_capacity (fds), pollTimeout);
|
||||||
|
} while (pollResult == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
throw_sys_if (pollResult == -1);
|
||||||
|
timeLeft -= pollTimeout;
|
||||||
|
|
||||||
|
if (fds[1].revents & (POLLIN | POLLHUP | POLLERR))
|
||||||
|
ReadAvailableData (errorFd, errOutput);
|
||||||
|
|
||||||
|
if (fds[0].revents & POLLIN)
|
||||||
|
{
|
||||||
|
uint8 ready;
|
||||||
|
ssize_t bytesRead;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bytesRead = read (outputFd, &ready, 1);
|
||||||
|
} while (bytesRead == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
if (bytesRead == 1 && ready == 0x33)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TerminateChildProcessAsync (childPid);
|
||||||
|
ThrowSerializedExceptionIfAny (errOutput);
|
||||||
|
throw ElevationFailed (SRC_POS, helperName, 1, ErrorOutputToString (errOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
int waitRes;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
waitRes = waitpid (childPid, &status, WNOHANG);
|
||||||
|
} while (waitRes == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
if (waitRes == childPid)
|
||||||
|
{
|
||||||
|
ReadAvailableData (errorFd, errOutput);
|
||||||
|
ThrowSerializedExceptionIfAny (errOutput);
|
||||||
|
int exitCode = WIFEXITED (status) ? WEXITSTATUS (status) : 1;
|
||||||
|
throw ElevationFailed (SRC_POS, helperName, exitCode, ErrorOutputToString (errOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw_sys_if (waitRes == -1);
|
||||||
|
|
||||||
|
if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL))
|
||||||
|
{
|
||||||
|
TerminateChildProcessAsync (childPid);
|
||||||
|
ThrowSerializedExceptionIfAny (errOutput);
|
||||||
|
throw ElevationFailed (SRC_POS, helperName, 1, ErrorOutputToString (errOutput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadAvailableData (errorFd, errOutput);
|
||||||
|
ThrowSerializedExceptionIfAny (errOutput);
|
||||||
|
string errorOutput = ErrorOutputToString (errOutput);
|
||||||
|
if (errorOutput.empty())
|
||||||
|
errorOutput = "Timed out while waiting for the elevated VeraCrypt service to start";
|
||||||
|
|
||||||
|
TerminateChildProcessAsync (childPid);
|
||||||
|
throw ElevationFailed (SRC_POS, helperName, 1, errorOutput);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TC_MACOSX
|
#ifdef TC_MACOSX
|
||||||
static bool IsMacOSXDevicePathWithPrefix (const string &path, const string &prefix)
|
static bool IsMacOSXDevicePathWithPrefix (const string &path, const string &prefix)
|
||||||
{
|
{
|
||||||
@@ -132,17 +529,18 @@ namespace VeraCrypt
|
|||||||
return unique_ptr <T> (dynamic_cast <T *> (deserializedObject.release()));
|
return unique_ptr <T> (dynamic_cast <T *> (deserializedObject.release()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreService::ProcessElevatedRequests ()
|
void CoreService::ProcessElevatedRequests (bool forkProcess)
|
||||||
{
|
{
|
||||||
int pid = fork();
|
int pid = forkProcess ? fork() : 0;
|
||||||
|
if (forkProcess)
|
||||||
throw_sys_if (pid == -1);
|
throw_sys_if (pid == -1);
|
||||||
|
|
||||||
if (pid == 0)
|
if (pid == 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int f = open ("/dev/null", 0);
|
if (forkProcess)
|
||||||
throw_sys_sub_if (f == -1, "/dev/null");
|
RedirectStandardErrorToDevNull ();
|
||||||
throw_sys_if (dup2 (f, STDERR_FILENO) == -1);
|
|
||||||
|
|
||||||
// Wait for sync code
|
// Wait for sync code
|
||||||
while (true)
|
while (true)
|
||||||
@@ -162,6 +560,21 @@ namespace VeraCrypt
|
|||||||
}
|
}
|
||||||
|
|
||||||
ElevatedPrivileges = true;
|
ElevatedPrivileges = true;
|
||||||
|
if (!forkProcess)
|
||||||
|
{
|
||||||
|
// Only the doas no-fork service emits a readiness byte, so the
|
||||||
|
// parent can distinguish a started service from a failed
|
||||||
|
// elevation. The sudo fork path keeps its original handshake
|
||||||
|
// (no readiness byte) to avoid altering its well-tested startup
|
||||||
|
// sequence.
|
||||||
|
uint8 ready = 0x33;
|
||||||
|
throw_sys_if (write (STDOUT_FILENO, &ready, 1) != 1);
|
||||||
|
|
||||||
|
// Startup diagnostics have been delivered. The parent closes
|
||||||
|
// errPipe after synchronization, so keep later service stderr
|
||||||
|
// writes away from a closed pipe.
|
||||||
|
RedirectStandardErrorToDevNull ();
|
||||||
|
}
|
||||||
ProcessRequests (STDIN_FILENO, STDOUT_FILENO);
|
ProcessRequests (STDIN_FILENO, STDOUT_FILENO);
|
||||||
_exit (0);
|
_exit (0);
|
||||||
}
|
}
|
||||||
@@ -178,6 +591,8 @@ namespace VeraCrypt
|
|||||||
|
|
||||||
void CoreService::ProcessRequests (int inputFD, int outputFD)
|
void CoreService::ProcessRequests (int inputFD, int outputFD)
|
||||||
{
|
{
|
||||||
|
finally_do ({ CloseDoasAuthTerminal (); });
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Core = move_ptr(CoreDirect);
|
Core = move_ptr(CoreDirect);
|
||||||
@@ -447,24 +862,16 @@ namespace VeraCrypt
|
|||||||
|
|
||||||
while (!ElevatedServiceAvailable)
|
while (!ElevatedServiceAvailable)
|
||||||
{
|
{
|
||||||
// Test if the user has an active "sudo" session.
|
// Test if the user has an active privilege helper session.
|
||||||
bool authCheckDone = false;
|
bool authCheckDone = false;
|
||||||
|
bool passwordCollected = false;
|
||||||
|
PrivilegeHelper privilegeHelper = FindPrivilegeHelper ();
|
||||||
if (!Core->GetUseDummySudoPassword ())
|
if (!Core->GetUseDummySudoPassword ())
|
||||||
{
|
{
|
||||||
// We are using -n to avoid prompting the user for a password.
|
// We are using -n to avoid prompting the user for a password.
|
||||||
// We are redirecting stderr to stdout and discarding both to avoid any output.
|
// We are redirecting stderr to stdout and discarding both to avoid any output.
|
||||||
// This approach also works on newer macOS versions (12.0 and later).
|
// This approach also works on newer macOS versions (12.0 and later).
|
||||||
std::string errorMsg;
|
std::string popenCommand = BuildPrivilegeHelperAuthCheckCommand (privilegeHelper);
|
||||||
|
|
||||||
string sudoAbsolutePath = Process::FindSystemBinary("sudo", errorMsg);
|
|
||||||
if (sudoAbsolutePath.empty())
|
|
||||||
throw SystemException(SRC_POS, errorMsg);
|
|
||||||
|
|
||||||
string trueAbsolutePath = Process::FindSystemBinary("true", errorMsg);
|
|
||||||
if (trueAbsolutePath.empty())
|
|
||||||
throw SystemException(SRC_POS, errorMsg);
|
|
||||||
|
|
||||||
std::string popenCommand = sudoAbsolutePath + " -n " + trueAbsolutePath + " > /dev/null 2>&1"; // We redirect stderr to stdout (2>&1) to be able to catch the result of the command
|
|
||||||
FILE* pipe = popen(popenCommand.c_str(), "r");
|
FILE* pipe = popen(popenCommand.c_str(), "r");
|
||||||
if (pipe)
|
if (pipe)
|
||||||
{
|
{
|
||||||
@@ -484,6 +891,7 @@ namespace VeraCrypt
|
|||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
(*AdminPasswordCallback)(request.AdminPassword);
|
(*AdminPasswordCallback)(request.AdminPassword);
|
||||||
|
passwordCollected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,8 +935,14 @@ namespace VeraCrypt
|
|||||||
|
|
||||||
request.FastElevation = false;
|
request.FastElevation = false;
|
||||||
|
|
||||||
if(!authCheckDone)
|
// doas persist is tty/session scoped. If a no-password
|
||||||
|
// attempt cannot reuse the caller tty, it may still fail
|
||||||
|
// and require a password retry on the authentication PTY.
|
||||||
|
if (!authCheckDone || (privilegeHelper.IsDoas() && !passwordCollected))
|
||||||
|
{
|
||||||
(*AdminPasswordCallback) (request.AdminPassword);
|
(*AdminPasswordCallback) (request.AdminPassword);
|
||||||
|
passwordCollected = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -562,6 +976,20 @@ namespace VeraCrypt
|
|||||||
|
|
||||||
void CoreService::StartElevated (const CoreServiceRequest &request)
|
void CoreService::StartElevated (const CoreServiceRequest &request)
|
||||||
{
|
{
|
||||||
|
PrivilegeHelper privilegeHelper = FindPrivilegeHelper ();
|
||||||
|
int doasAuthTerminal = -1;
|
||||||
|
string doasAuthTerminalPath;
|
||||||
|
bool doasNoPasswordAttempt = privilegeHelper.IsDoas() && request.AdminPassword.empty();
|
||||||
|
bool useCallerDoasTerminal = doasNoPasswordAttempt && HasControllingTerminal();
|
||||||
|
|
||||||
|
if (privilegeHelper.IsDoas() && !useCallerDoasTerminal)
|
||||||
|
{
|
||||||
|
doasAuthTerminal = OpenDoasAuthTerminal (doasAuthTerminalPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool elevatedServiceStarted = false;
|
||||||
|
finally_do_arg2 (bool *, &elevatedServiceStarted, int *, &doasAuthTerminal, { if (!*finally_arg && *finally_arg2 != -1) { close (*finally_arg2); *finally_arg2 = -1; } });
|
||||||
|
|
||||||
unique_ptr <Pipe> inPipe (new Pipe());
|
unique_ptr <Pipe> inPipe (new Pipe());
|
||||||
unique_ptr <Pipe> outPipe (new Pipe());
|
unique_ptr <Pipe> outPipe (new Pipe());
|
||||||
Pipe errPipe;
|
Pipe errPipe;
|
||||||
@@ -575,12 +1003,7 @@ namespace VeraCrypt
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Throw exception if sudo is not found in secure locations
|
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
string sudoPath = Process::FindSystemBinary("sudo", errorMsg);
|
|
||||||
if (sudoPath.empty())
|
|
||||||
throw SystemException(SRC_POS, errorMsg);
|
|
||||||
|
|
||||||
string appPath = request.ApplicationExecutablePath;
|
string appPath = request.ApplicationExecutablePath;
|
||||||
// if appPath is empty or not absolute, use FindSystemBinary to get the full path of veracrpyt executable
|
// if appPath is empty or not absolute, use FindSystemBinary to get the full path of veracrpyt executable
|
||||||
if (appPath.empty() || appPath[0] != '/')
|
if (appPath.empty() || appPath[0] != '/')
|
||||||
@@ -592,21 +1015,31 @@ namespace VeraCrypt
|
|||||||
|
|
||||||
#if defined(TC_LINUX)
|
#if defined(TC_LINUX)
|
||||||
// AppImage specific handling:
|
// AppImage specific handling:
|
||||||
// If running from an AppImage, use the path to the AppImage file itself for sudo.
|
// If running from an AppImage, use the path to the AppImage file itself for the privilege helper.
|
||||||
const char* appImageEnv = getenv("APPIMAGE");
|
const char* appImageEnv = getenv("APPIMAGE");
|
||||||
|
|
||||||
if (Process::IsRunningUnderAppImage(appPath) && appImageEnv != NULL)
|
if (Process::IsRunningUnderAppImage(appPath) && appImageEnv != NULL)
|
||||||
{
|
{
|
||||||
// The path to the AppImage file is stored in the APPIMAGE environment variable.
|
// The path to the AppImage file is stored in the APPIMAGE environment variable.
|
||||||
// We need to use this path for sudo to work correctly.
|
// We need to use this path for elevation to work correctly.
|
||||||
appPath = appImageEnv;
|
appPath = appImageEnv;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (privilegeHelper.IsDoas() && !useCallerDoasTerminal)
|
||||||
|
{
|
||||||
|
AttachDoasAuthTerminal (doasAuthTerminalPath);
|
||||||
|
}
|
||||||
|
if (doasAuthTerminal != -1)
|
||||||
|
close (doasAuthTerminal);
|
||||||
|
|
||||||
|
throw_sys_if (dup2 (errPipe.GetWriteFD(), STDERR_FILENO) == -1);
|
||||||
throw_sys_if (dup2 (inPipe->GetReadFD(), STDIN_FILENO) == -1);
|
throw_sys_if (dup2 (inPipe->GetReadFD(), STDIN_FILENO) == -1);
|
||||||
throw_sys_if (dup2 (outPipe->GetWriteFD(), STDOUT_FILENO) == -1);
|
throw_sys_if (dup2 (outPipe->GetWriteFD(), STDOUT_FILENO) == -1);
|
||||||
throw_sys_if (dup2 (errPipe.GetWriteFD(), STDERR_FILENO) == -1);
|
|
||||||
|
|
||||||
const char *args[] = { sudoPath.c_str(), "-S", "-p", "", appPath.c_str(), TC_CORE_SERVICE_CMDLINE_OPTION, nullptr };
|
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 };
|
||||||
|
const char *doasNoPasswordArgs[] = { privilegeHelper.Path.c_str(), "-n", appPath.c_str(), TC_CORE_SERVICE_NO_FORK_CMDLINE_OPTION, nullptr };
|
||||||
|
const char **args = privilegeHelper.IsDoas() ? (doasNoPasswordAttempt ? doasNoPasswordArgs : doasArgs) : sudoArgs;
|
||||||
execvp (args[0], ((char* const*) args));
|
execvp (args[0], ((char* const*) args));
|
||||||
throw SystemException (SRC_POS, args[0]);
|
throw SystemException (SRC_POS, args[0]);
|
||||||
}
|
}
|
||||||
@@ -636,10 +1069,13 @@ namespace VeraCrypt
|
|||||||
_exit (1);
|
_exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serviceInputFd = inPipe->GetWriteFD();
|
||||||
|
int serviceOutputFd = outPipe->GetReadFD();
|
||||||
|
|
||||||
vector <char> adminPassword (request.AdminPassword.size() + 1);
|
vector <char> adminPassword (request.AdminPassword.size() + 1);
|
||||||
int timeout = 6000;
|
int timeout = 6000;
|
||||||
|
|
||||||
// 'request.FastElevation' is always false under Linux / FreeBSD when "sudo -n" works properly
|
// 'request.FastElevation' is always false under Linux / FreeBSD when non-interactive auth checks work properly
|
||||||
if (request.FastElevation)
|
if (request.FastElevation)
|
||||||
{
|
{
|
||||||
string dummyPassword = "dummy\n";
|
string dummyPassword = "dummy\n";
|
||||||
@@ -654,20 +1090,48 @@ namespace VeraCrypt
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(TC_LINUX )
|
#if defined(TC_LINUX )
|
||||||
Thread::Sleep (1000); // wait 1 second for the forked sudo to start
|
Thread::Sleep (1000); // wait 1 second for the forked privilege helper to start
|
||||||
#endif
|
#endif
|
||||||
if (write (inPipe->GetWriteFD(), &adminPassword.front(), adminPassword.size())) { } // Errors ignored
|
if (privilegeHelper.IsSudo())
|
||||||
|
{
|
||||||
|
if (write (serviceInputFd, &adminPassword.front(), adminPassword.size())) { } // Errors ignored
|
||||||
|
}
|
||||||
|
else if (doasAuthTerminal != -1 && !doasNoPasswordAttempt)
|
||||||
|
{
|
||||||
|
// doas reads authentication from the controlling terminal, not stdin.
|
||||||
|
WriteAllBestEffort (doasAuthTerminal, &adminPassword.front(), adminPassword.size());
|
||||||
|
}
|
||||||
|
|
||||||
burn (&adminPassword.front(), adminPassword.size());
|
burn (&adminPassword.front(), adminPassword.size());
|
||||||
|
|
||||||
throw_sys_if (fcntl (outPipe->GetReadFD(), F_SETFL, O_NONBLOCK) == -1);
|
throw_sys_if (fcntl (serviceOutputFd, F_SETFL, O_NONBLOCK) == -1);
|
||||||
throw_sys_if (fcntl (errPipe.GetReadFD(), F_SETFL, O_NONBLOCK) == -1);
|
throw_sys_if (fcntl (errPipe.GetReadFD(), F_SETFL, O_NONBLOCK) == -1);
|
||||||
|
|
||||||
|
if (privilegeHelper.IsDoas())
|
||||||
|
{
|
||||||
|
shared_ptr <Stream> inputStream (new FileStream (serviceInputFd));
|
||||||
|
shared_ptr <Stream> outputStream (new FileStream (serviceOutputFd));
|
||||||
|
|
||||||
|
SendElevatedServiceSyncWithTimeout (inputStream, serviceOutputFd, errPipe.GetReadFD(), forkedPid, privilegeHelper.Name, timeout);
|
||||||
|
throw_sys_if (fcntl (serviceOutputFd, F_SETFL, 0) == -1);
|
||||||
|
ReapChildProcessAsync (forkedPid);
|
||||||
|
|
||||||
|
ServiceInputStream = inputStream;
|
||||||
|
ServiceOutputStream = outputStream;
|
||||||
|
|
||||||
|
AdminInputPipe = move_ptr(inPipe);
|
||||||
|
AdminOutputPipe = move_ptr(outPipe);
|
||||||
|
DoasAuthTerminalFd = doasAuthTerminal;
|
||||||
|
doasAuthTerminal = -1;
|
||||||
|
elevatedServiceStarted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
vector <char> errOutput;
|
vector <char> errOutput;
|
||||||
errOutput.reserve (4096);
|
errOutput.reserve (4096);
|
||||||
|
|
||||||
Poller poller (outPipe->GetReadFD(), errPipe.GetReadFD());
|
Poller poller (serviceOutputFd, errPipe.GetReadFD());
|
||||||
int status, waitRes;
|
int status, waitRes;
|
||||||
int exitCode = 1;
|
int exitCode = 1;
|
||||||
|
|
||||||
@@ -704,7 +1168,7 @@ namespace VeraCrypt
|
|||||||
outPipe->Close();
|
outPipe->Close();
|
||||||
errPipe.Close();
|
errPipe.Close();
|
||||||
|
|
||||||
// 'request.FastElevation' is always false under Linux / FreeBSD
|
// 'request.FastElevation' is always false under Linux / FreeBSD when non-interactive auth checks work properly
|
||||||
if (request.FastElevation)
|
if (request.FastElevation)
|
||||||
{
|
{
|
||||||
// Prevent defunct process
|
// Prevent defunct process
|
||||||
@@ -721,30 +1185,16 @@ namespace VeraCrypt
|
|||||||
};
|
};
|
||||||
Thread thread;
|
Thread thread;
|
||||||
thread.Start (new WaitFunctor (forkedPid));
|
thread.Start (new WaitFunctor (forkedPid));
|
||||||
|
thread.Detach ();
|
||||||
|
|
||||||
throw ElevationFailed (SRC_POS, "sudo", 1, "");
|
throw ElevationFailed (SRC_POS, privilegeHelper.Name, 1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
waitRes = waitpid (forkedPid, &status, 0);
|
waitRes = waitpid (forkedPid, &status, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!errOutput.empty())
|
ThrowSerializedExceptionIfAny (errOutput);
|
||||||
{
|
|
||||||
unique_ptr <Serializable> deserializedObject;
|
|
||||||
Exception *deserializedException = nullptr;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
shared_ptr <Stream> stream (new MemoryStream (ConstBufferPtr ((uint8 *) &errOutput[0], errOutput.size())));
|
|
||||||
deserializedObject.reset (Serializable::DeserializeNew (stream));
|
|
||||||
deserializedException = dynamic_cast <Exception*> (deserializedObject.get());
|
|
||||||
}
|
|
||||||
catch (...) { }
|
|
||||||
|
|
||||||
if (deserializedException)
|
|
||||||
deserializedException->Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw_sys_if (waitRes == -1);
|
throw_sys_if (waitRes == -1);
|
||||||
exitCode = (WIFEXITED (status) ? WEXITSTATUS (status) : 1);
|
exitCode = (WIFEXITED (status) ? WEXITSTATUS (status) : 1);
|
||||||
@@ -756,23 +1206,27 @@ namespace VeraCrypt
|
|||||||
strErrOutput.insert (strErrOutput.begin(), errOutput.begin(), errOutput.end());
|
strErrOutput.insert (strErrOutput.begin(), errOutput.begin(), errOutput.end());
|
||||||
|
|
||||||
// sudo may require a tty even if -S is used
|
// sudo may require a tty even if -S is used
|
||||||
if (strErrOutput.find (" tty") != string::npos)
|
if (privilegeHelper.IsSudo() && strErrOutput.find (" tty") != string::npos)
|
||||||
strErrOutput += "\nTo enable use of 'sudo' by applications without a terminal window, please disable 'requiretty' option in '/etc/sudoers'. Newer versions of sudo automatically determine whether a terminal is required ('requiretty' option is obsolete).";
|
strErrOutput += "\nTo enable use of 'sudo' by applications without a terminal window, please disable 'requiretty' option in '/etc/sudoers'. Newer versions of sudo automatically determine whether a terminal is required ('requiretty' option is obsolete).";
|
||||||
|
|
||||||
throw ElevationFailed (SRC_POS, "sudo", exitCode, strErrOutput);
|
throw ElevationFailed (SRC_POS, privilegeHelper.Name, exitCode, strErrOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_sys_if (fcntl (outPipe->GetReadFD(), F_SETFL, 0) == -1);
|
throw_sys_if (fcntl (serviceOutputFd, F_SETFL, 0) == -1);
|
||||||
|
|
||||||
ServiceInputStream = shared_ptr <Stream> (new FileStream (inPipe->GetWriteFD()));
|
if (privilegeHelper.IsSudo())
|
||||||
ServiceOutputStream = shared_ptr <Stream> (new FileStream (outPipe->GetReadFD()));
|
{
|
||||||
|
ServiceInputStream = shared_ptr <Stream> (new FileStream (serviceInputFd));
|
||||||
|
ServiceOutputStream = shared_ptr <Stream> (new FileStream (serviceOutputFd));
|
||||||
|
}
|
||||||
|
|
||||||
// Send sync code
|
// Send sync code (sudo path keeps the original fire-and-forget handshake)
|
||||||
uint8 sync[] = { 0, 0x11, 0x22 };
|
uint8 sync[] = { 0, 0x11, 0x22 };
|
||||||
ServiceInputStream->Write (ConstBufferPtr (sync, array_capacity (sync)));
|
ServiceInputStream->Write (ConstBufferPtr (sync, array_capacity (sync)));
|
||||||
|
|
||||||
AdminInputPipe = move_ptr(inPipe);
|
AdminInputPipe = move_ptr(inPipe);
|
||||||
AdminOutputPipe = move_ptr(outPipe);
|
AdminOutputPipe = move_ptr(outPipe);
|
||||||
|
elevatedServiceStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreService::Stop ()
|
void CoreService::Stop ()
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace VeraCrypt
|
|||||||
class CoreService
|
class CoreService
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void ProcessElevatedRequests ();
|
static void ProcessElevatedRequests (bool forkProcess = true);
|
||||||
static void ProcessRequests (int inputFD = -1, int outputFD = -1);
|
static void ProcessRequests (int inputFD = -1, int outputFD = -1);
|
||||||
static void RequestCheckFilesystem (shared_ptr <VolumeInfo> mountedVolume, bool repair);
|
static void RequestCheckFilesystem (shared_ptr <VolumeInfo> mountedVolume, bool repair);
|
||||||
static void RequestDismountFilesystem (const DirectoryPath &mountPoint, bool force);
|
static void RequestDismountFilesystem (const DirectoryPath &mountPoint, bool force);
|
||||||
@@ -70,6 +70,7 @@ namespace VeraCrypt
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define TC_CORE_SERVICE_CMDLINE_OPTION "--core-service"
|
#define TC_CORE_SERVICE_CMDLINE_OPTION "--core-service"
|
||||||
|
#define TC_CORE_SERVICE_NO_FORK_CMDLINE_OPTION "--core-service-no-fork"
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // TC_HEADER_Core_Unix_CoreService
|
#endif // TC_HEADER_Core_Unix_CoreService
|
||||||
|
|||||||
@@ -21,14 +21,12 @@
|
|||||||
#ifdef TC_LINUX
|
#ifdef TC_LINUX
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
#include <pwd.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "Platform/FileStream.h"
|
#include "Platform/FileStream.h"
|
||||||
#include "Platform/MemoryStream.h"
|
#include "Platform/MemoryStream.h"
|
||||||
#include "Platform/SystemLog.h"
|
#include "Platform/SystemLog.h"
|
||||||
|
#include "Core/Unix/UnixUser.h"
|
||||||
#include "Driver/Fuse/FuseService.h"
|
#include "Driver/Fuse/FuseService.h"
|
||||||
#include "Volume/VolumePasswordCache.h"
|
#include "Volume/VolumePasswordCache.h"
|
||||||
|
|
||||||
@@ -43,26 +41,6 @@ namespace VeraCrypt
|
|||||||
static bool SamePath (const string& path1, const string& path2);
|
static bool SamePath (const string& path1, const string& path2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
static bool GetDoasUserIds (uid_t *uid, gid_t *gid)
|
|
||||||
{
|
|
||||||
const char *env = getenv ("DOAS_USER");
|
|
||||||
if (!env || !env[0])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
struct passwd *pw = getpwnam (env);
|
|
||||||
if (!pw)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (uid)
|
|
||||||
*uid = pw->pw_uid;
|
|
||||||
if (gid)
|
|
||||||
*gid = pw->pw_gid;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Struct to hold terminal emulator information
|
// Struct to hold terminal emulator information
|
||||||
struct TerminalInfo {
|
struct TerminalInfo {
|
||||||
const char* name;
|
const char* name;
|
||||||
@@ -657,11 +635,9 @@ namespace VeraCrypt
|
|||||||
catch (...) { }
|
catch (...) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
gid_t doasGid;
|
gid_t doasGid;
|
||||||
if (GetDoasUserIds (nullptr, &doasGid))
|
if (GetDoasUserIds (nullptr, &doasGid))
|
||||||
return doasGid;
|
return doasGid;
|
||||||
#endif
|
|
||||||
|
|
||||||
return getgid();
|
return getgid();
|
||||||
}
|
}
|
||||||
@@ -679,11 +655,9 @@ namespace VeraCrypt
|
|||||||
catch (...) { }
|
catch (...) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
uid_t doasUid;
|
uid_t doasUid;
|
||||||
if (GetDoasUserIds (&doasUid, nullptr))
|
if (GetDoasUserIds (&doasUid, nullptr))
|
||||||
return doasUid;
|
return doasUid;
|
||||||
#endif
|
|
||||||
|
|
||||||
return getuid();
|
return getuid();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Derived from source code of TrueCrypt 7.1a, which is
|
||||||
|
Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed
|
||||||
|
by the TrueCrypt License 3.0.
|
||||||
|
|
||||||
|
Modifications and additions to the original source code (contained in this file)
|
||||||
|
and all other portions of this file are Copyright (c) 2013-2026 AM Crypto
|
||||||
|
and are governed by the Apache License 2.0 the full text of which is
|
||||||
|
contained in the file License.txt included in VeraCrypt binary and source
|
||||||
|
code distribution packages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TC_HEADER_Core_Unix_UnixUser
|
||||||
|
#define TC_HEADER_Core_Unix_UnixUser
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define TC_DOAS_CORE_SERVICE_ENV "VERACRYPT_DOAS_CORE_SERVICE"
|
||||||
|
|
||||||
|
namespace VeraCrypt
|
||||||
|
{
|
||||||
|
static inline bool GetDoasUserIds (uid_t *uid, gid_t *gid)
|
||||||
|
{
|
||||||
|
if (getuid () != 0 || geteuid () != 0 || getenv ("SUDO_UID") || getenv ("SUDO_GID"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifndef TC_OPENBSD
|
||||||
|
const char *trustedDoasService = getenv (TC_DOAS_CORE_SERVICE_ENV);
|
||||||
|
if (!trustedDoasService || strcmp (trustedDoasService, "1") != 0)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *env = getenv ("DOAS_USER");
|
||||||
|
if (!env || !env[0])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
long bufferSize = 16384;
|
||||||
|
#ifdef _SC_GETPW_R_SIZE_MAX
|
||||||
|
long sysconfBufferSize = sysconf (_SC_GETPW_R_SIZE_MAX);
|
||||||
|
if (sysconfBufferSize > 0)
|
||||||
|
bufferSize = sysconfBufferSize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct passwd pw;
|
||||||
|
struct passwd *pwResult = nullptr;
|
||||||
|
std::vector <char> buffer (static_cast <size_t> (bufferSize));
|
||||||
|
int status;
|
||||||
|
|
||||||
|
while ((status = getpwnam_r (env, &pw, &buffer[0], buffer.size(), &pwResult)) == ERANGE)
|
||||||
|
{
|
||||||
|
if (buffer.size () > 1024 * 1024)
|
||||||
|
return false;
|
||||||
|
buffer.resize (buffer.size () * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != 0 || !pwResult)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (uid)
|
||||||
|
*uid = pw.pw_uid;
|
||||||
|
if (gid)
|
||||||
|
*gid = pw.pw_gid;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TC_HEADER_Core_Unix_UnixUser
|
||||||
@@ -40,9 +40,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
#include <pwd.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@@ -55,6 +52,7 @@
|
|||||||
#include "Platform/SystemLog.h"
|
#include "Platform/SystemLog.h"
|
||||||
#include "Platform/Unix/Pipe.h"
|
#include "Platform/Unix/Pipe.h"
|
||||||
#include "Platform/Unix/Poller.h"
|
#include "Platform/Unix/Poller.h"
|
||||||
|
#include "Core/Unix/UnixUser.h"
|
||||||
#include "Volume/EncryptionThreadPool.h"
|
#include "Volume/EncryptionThreadPool.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
|
||||||
@@ -68,26 +66,6 @@ namespace VeraCrypt
|
|||||||
static const uint64 VC_FUSE_METADATA_SIZE = 64 * 1024;
|
static const uint64 VC_FUSE_METADATA_SIZE = 64 * 1024;
|
||||||
static const uint64 VC_FUSE_STAT_BLOCK_SIZE = 512;
|
static const uint64 VC_FUSE_STAT_BLOCK_SIZE = 512;
|
||||||
|
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
static bool fuse_service_get_doas_user_ids (uid_t *uid, gid_t *gid)
|
|
||||||
{
|
|
||||||
const char *env = getenv ("DOAS_USER");
|
|
||||||
if (!env || !env[0])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
struct passwd *pw = getpwnam (env);
|
|
||||||
if (!pw)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (uid)
|
|
||||||
*uid = pw->pw_uid;
|
|
||||||
if (gid)
|
|
||||||
*gid = pw->pw_gid;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static uint64 fuse_service_ceil_div (uint64 value, uint64 divisor)
|
static uint64 fuse_service_ceil_div (uint64 value, uint64 divisor)
|
||||||
{
|
{
|
||||||
return (value / divisor) + ((value % divisor) ? 1 : 0);
|
return (value / divisor) + ((value % divisor) ? 1 : 0);
|
||||||
@@ -813,18 +791,16 @@ namespace VeraCrypt
|
|||||||
}
|
}
|
||||||
catch (...) { }
|
catch (...) { }
|
||||||
}
|
}
|
||||||
#ifdef TC_OPENBSD
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uid_t doasUid;
|
uid_t doasUid;
|
||||||
gid_t doasGid;
|
gid_t doasGid;
|
||||||
if (fuse_service_get_doas_user_ids (&doasUid, &doasGid))
|
if (GetDoasUserIds (&doasUid, &doasGid))
|
||||||
{
|
{
|
||||||
FuseService::UserId = doasUid;
|
FuseService::UserId = doasUid;
|
||||||
FuseService::GroupId = doasGid;
|
FuseService::GroupId = doasGid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static fuse_operations fuse_service_oper;
|
static fuse_operations fuse_service_oper;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "Platform/SystemLog.h"
|
#include "Platform/SystemLog.h"
|
||||||
#include "Volume/EncryptionThreadPool.h"
|
#include "Volume/EncryptionThreadPool.h"
|
||||||
#include "Core/Unix/CoreService.h"
|
#include "Core/Unix/CoreService.h"
|
||||||
|
#include "Core/Unix/UnixUser.h"
|
||||||
#include "Main/Application.h"
|
#include "Main/Application.h"
|
||||||
#include "Main/Main.h"
|
#include "Main/Main.h"
|
||||||
#include "Main/UserInterface.h"
|
#include "Main/UserInterface.h"
|
||||||
@@ -43,12 +44,16 @@ int main (int argc, char **argv)
|
|||||||
|
|
||||||
setenv ("PATH", sysPathStr.c_str(), 1);
|
setenv ("PATH", sysPathStr.c_str(), 1);
|
||||||
|
|
||||||
if (argc > 1 && strcmp (argv[1], TC_CORE_SERVICE_CMDLINE_OPTION) == 0)
|
if (argc > 1 && (strcmp (argv[1], TC_CORE_SERVICE_CMDLINE_OPTION) == 0 || strcmp (argv[1], TC_CORE_SERVICE_NO_FORK_CMDLINE_OPTION) == 0))
|
||||||
{
|
{
|
||||||
// Process elevated requests
|
// Process elevated requests
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CoreService::ProcessElevatedRequests();
|
bool forkProcess = strcmp (argv[1], TC_CORE_SERVICE_CMDLINE_OPTION) == 0;
|
||||||
|
if (!forkProcess)
|
||||||
|
setenv (TC_DOAS_CORE_SERVICE_ENV, "1", 1);
|
||||||
|
|
||||||
|
CoreService::ProcessElevatedRequests (forkProcess);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (exception &e)
|
catch (exception &e)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace VeraCrypt
|
|||||||
virtual ~Thread () { };
|
virtual ~Thread () { };
|
||||||
|
|
||||||
void Join () const;
|
void Join () const;
|
||||||
|
void Detach () const;
|
||||||
void Start (ThreadProcPtr threadProc, void *parameter = nullptr);
|
void Start (ThreadProcPtr threadProc, void *parameter = nullptr);
|
||||||
|
|
||||||
void Start (Functor *functor)
|
void Start (Functor *functor)
|
||||||
|
|||||||
@@ -45,6 +45,13 @@ namespace VeraCrypt
|
|||||||
throw SystemException (SRC_POS, status);
|
throw SystemException (SRC_POS, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::Detach () const
|
||||||
|
{
|
||||||
|
int status = pthread_detach (SystemHandle);
|
||||||
|
if (status != 0)
|
||||||
|
throw SystemException (SRC_POS, status);
|
||||||
|
}
|
||||||
|
|
||||||
void Thread::Start (ThreadProcPtr threadProc, void *parameter)
|
void Thread::Start (ThreadProcPtr threadProc, void *parameter)
|
||||||
{
|
{
|
||||||
PthreadAttr attr;
|
PthreadAttr attr;
|
||||||
|
|||||||
Reference in New Issue
Block a user