Files
VeraCrypt/Tests/fuset_startup_faults.c
Mounir IDRASSI 41bc8e5f6a macOS: isolate FUSE-T auxiliary mount paths
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.
2026-09-29 10:01:37 +02:00

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);