mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-10-06 00:56:35 -05:00
Create a fresh private parent for each FUSE-T auxiliary mount. Elevated parents stay root-owned and grant only the original user read/search access, restricting access through the auxiliary mount path and preventing caller-controlled path replacement during elevated setup. Clear inherited ACLs and reject temporary filesystems that ignore ownership. Remove per-mount parents on setup failure and service teardown, while preserving legacy temporary directories and discovery behavior. Add regression coverage for permissions, unique parent creation, rollback, and cleanup. FUSE-T transport authentication is outside this change.
153 lines
3.9 KiB
C
153 lines
3.9 KiB
C
/* Scoped macOS fault injection for test_fuset_dismount.py --startup-faults
|
|
* and test_macos_gui_teardown.py (delayed service teardown).
|
|
* Copyright (c) 2026 AM Crypto. Licensed under the Apache License 2.0.
|
|
*/
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
|
|
static int mode_is (const char *mode)
|
|
{
|
|
const char *fault = getenv ("VC_FUSET_TEST_FAULT");
|
|
return fault && strcmp (fault, mode) == 0;
|
|
}
|
|
|
|
static int fixture_path (const char *path, const char *suffix)
|
|
{
|
|
const char *root = getenv ("VC_FUSET_TEST_ROOT");
|
|
size_t size = strlen (path), suffix_size = strlen (suffix);
|
|
return root && strncmp (path, root, strlen (root)) == 0
|
|
&& path[strlen (root)] == '/' && size >= suffix_size
|
|
&& strcmp (path + size - suffix_size, suffix) == 0;
|
|
}
|
|
|
|
static void mark_fault (void)
|
|
{
|
|
const char *path = getenv ("VC_FUSET_TEST_FAULT_MARKER");
|
|
if (path)
|
|
{
|
|
int fd = open (path, O_WRONLY | O_CREAT, 0600);
|
|
if (fd != -1)
|
|
close (fd);
|
|
}
|
|
}
|
|
|
|
static int test_connect (int fd, const struct sockaddr *address, socklen_t length)
|
|
{
|
|
static const char prefix[] = "/private/tmp/.veracrypt-shutdown-";
|
|
if ((mode_is ("refused") || mode_is ("rollback-blocked")) && address->sa_family == AF_UNIX
|
|
&& strncmp (((const struct sockaddr_un *) address)->sun_path, prefix, sizeof (prefix) - 1) == 0)
|
|
{
|
|
mark_fault();
|
|
const char *log = getenv ("VC_FUSET_TEST_SOCKET_LOG");
|
|
if (log)
|
|
{
|
|
int output = open (log, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
if (output != -1)
|
|
{
|
|
const char *path = ((const struct sockaddr_un *) address)->sun_path;
|
|
write (output, path, strlen (path));
|
|
close (output);
|
|
}
|
|
}
|
|
errno = ECONNREFUSED;
|
|
return -1;
|
|
}
|
|
return connect (fd, address, length);
|
|
}
|
|
|
|
static int test_open (const char *path, int flags, ...)
|
|
{
|
|
if (mode_is ("metadata") && fixture_path (path, "/shutdown-socket"))
|
|
{
|
|
mark_fault();
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
if (flags & O_CREAT)
|
|
{
|
|
va_list args;
|
|
va_start (args, flags);
|
|
int mode = va_arg (args, int);
|
|
va_end (args);
|
|
return open (path, flags, mode);
|
|
}
|
|
return open (path, flags);
|
|
}
|
|
|
|
static int test_stat (const char *path, struct stat *value)
|
|
{
|
|
if (mode_is ("control") && fixture_path (path, "/control"))
|
|
{
|
|
mark_fault();
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
return stat (path, value);
|
|
}
|
|
|
|
static char *test_mkdtemp (char *path)
|
|
{
|
|
if (mode_is ("aux-child") && fixture_path (path, "/.veracrypt_aux_mnt-XXXXXXXXXXXX"))
|
|
{
|
|
mark_fault();
|
|
errno = EACCES;
|
|
return NULL;
|
|
}
|
|
return mkdtemp (path);
|
|
}
|
|
|
|
static unsigned fault_delay (void)
|
|
{
|
|
const char *delay = getenv ("VC_FUSET_TEST_DELAY");
|
|
return delay ? (unsigned) atoi (delay) : 10;
|
|
}
|
|
|
|
static int test_unmount (const char *path, int flags)
|
|
{
|
|
const char *gate = getenv ("VC_FUSET_TEST_UNMOUNT_GATE");
|
|
if (mode_is ("rollback-blocked") && gate && fixture_path (path, "") && access (gate, F_OK) == 0)
|
|
{
|
|
mark_fault();
|
|
errno = EBUSY;
|
|
return -1;
|
|
}
|
|
if (mode_is ("unmount-delay") && fixture_path (path, ""))
|
|
{
|
|
/* The service removes SMB slowly; its caller waits for the reply. */
|
|
mark_fault();
|
|
sleep (fault_delay());
|
|
}
|
|
return unmount (path, flags);
|
|
}
|
|
|
|
static int test_unlinkat (int fd, const char *path, int flags)
|
|
{
|
|
if (mode_is ("cleanup-delay") && (flags & AT_REMOVEDIR) && strstr (path, ".veracrypt_aux_mnt"))
|
|
{
|
|
/* The service outlives its caller's exit wait after SMB removal. */
|
|
mark_fault();
|
|
sleep (fault_delay());
|
|
}
|
|
return unlinkat (fd, path, flags);
|
|
}
|
|
|
|
#define INTERPOSE(replacement, original) \
|
|
__attribute__((used)) static const struct { const void *replace; const void *replacee; } \
|
|
interpose_##original __attribute__((section("__DATA,__interpose"))) = { \
|
|
(const void *) replacement, (const void *) original }
|
|
|
|
INTERPOSE (test_connect, connect);
|
|
INTERPOSE (test_open, open);
|
|
INTERPOSE (test_stat, stat);
|
|
INTERPOSE (test_mkdtemp, mkdtemp);
|
|
INTERPOSE (test_unmount, unmount);
|
|
INTERPOSE (test_unlinkat, unlinkat);
|