Files
VeraCrypt/src/Main/MacOSXFormatterDevice.h
T
Mounir IDRASSI 610feb4c28 macOS: block partitioned disk alias bypass
On macOS, the same whole disk can be addressed as both /dev/diskN and /dev/rdiskN. The GUI creation wizard only compared the selected path against the enumerated raw device path, so manually entering the block-device alias could bypass the existing DEVICE_PARTITIONS_ERR guard and allow formatting a disk that still had partitions.

Add a shared macOS device-path comparison helper that normalizes paths to their raw-device form before comparison. Use it in the GUI wizard so /dev/diskN and /dev/rdiskN are treated as the same whole-disk target while partition paths remain distinct.

Apply the same partitioned whole-device guard in the text/CLI creation path as well, including the macOS alias normalization, so command-line creation cannot format a partitioned top-level disk through an alternate device alias.

Fixes #728
2026-05-29 18:32:32 +09:00

195 lines
5.5 KiB
C++

/*
VeraCrypt source code
Copyright (c) 2026 AM Crypto
This file is part of VeraCrypt and is 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 VC_HEADER_Main_MacOSXFormatterDevice
#define VC_HEADER_Main_MacOSXFormatterDevice
#include "Main/Main.h"
#ifdef TC_MACOSX
#include <unistd.h>
#include "Core/Unix/CoreService.h"
#include "Platform/Unix/Process.h"
namespace VeraCrypt
{
inline bool IsMacOSXDiskDevicePath (const string &deviceIdentifier, const string &prefix)
{
return deviceIdentifier.find (prefix) == 0
&& deviceIdentifier.size() > prefix.size()
&& deviceIdentifier[prefix.size()] >= '0'
&& deviceIdentifier[prefix.size()] <= '9';
}
inline string GetMacOSXRawDevicePath (const string &deviceIdentifier)
{
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/rdisk"))
return deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/disk"))
return string ("/dev/rdisk") + deviceIdentifier.substr (9);
if (IsMacOSXDiskDevicePath (deviceIdentifier, "rdisk"))
return string ("/dev/") + deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "disk"))
return string ("/dev/r") + deviceIdentifier;
return deviceIdentifier;
}
inline string GetMacOSXBlockDevicePath (const string &deviceIdentifier)
{
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/disk"))
return deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/rdisk"))
return string ("/dev/disk") + deviceIdentifier.substr (10);
if (IsMacOSXDiskDevicePath (deviceIdentifier, "disk"))
return string ("/dev/") + deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "rdisk"))
return string ("/dev/disk") + deviceIdentifier.substr (5);
return deviceIdentifier;
}
inline bool IsSameMacOSXDevicePath (const string &firstDeviceIdentifier, const string &secondDeviceIdentifier)
{
return GetMacOSXRawDevicePath (firstDeviceIdentifier) == GetMacOSXRawDevicePath (secondDeviceIdentifier);
}
inline string GetMacOSXFormatterName (const string &fsFormatter)
{
size_t namePos = fsFormatter.find_last_of ('/');
return namePos == string::npos ? fsFormatter : fsFormatter.substr (namePos + 1);
}
inline bool IsMacOSXAPFSFormatter (const string &fsFormatter)
{
return GetMacOSXFormatterName (fsFormatter) == "newfs_apfs";
}
inline bool IsMacOSXExFATFormatter (const string &fsFormatter)
{
return GetMacOSXFormatterName (fsFormatter) == "newfs_exfat";
}
inline bool UseElevatedMacOSXAPFSFormatter (const string &fsFormatter)
{
return IsMacOSXAPFSFormatter (fsFormatter) && !Core->HasAdminPrivileges();
}
inline void AddMacOSXAPFSFormatterUserArgs (list <string> &args)
{
stringstream uid;
stringstream gid;
// The APFS formatter may run elevated, so preserve the invoking user's ownership.
uid << getuid();
gid << getgid();
args.push_back ("-U");
args.push_back (uid.str());
args.push_back ("-G");
args.push_back (gid.str());
}
inline void AddMacOSXExFATFormatterArgs (list <string> &args)
{
// Match Disk Utility/Finder erase behavior by deriving a fresh exFAT layout.
args.push_back ("-R");
}
struct MacOSXFormatterDeviceOwnerRestore
{
MacOSXFormatterDeviceOwnerRestore (const FilesystemPath &path, const UserId &owner)
: Path (path), Owner (owner) { }
FilesystemPath Path;
UserId Owner;
};
typedef list <MacOSXFormatterDeviceOwnerRestore> MacOSXFormatterDeviceOwnerRestoreList;
inline void AddUniqueMacOSXDevicePath (list <FilesystemPath> &paths, const string &path)
{
if (path.empty())
return;
foreach (const FilesystemPath &existingPath, paths)
{
if (string (existingPath) == path)
return;
}
paths.push_back (FilesystemPath (path));
}
inline void PrepareMacOSXFormatterDevice (const DevicePath &devicePath, MacOSXFormatterDeviceOwnerRestoreList &changedDeviceOwners)
{
if (Core->HasAdminPrivileges())
return;
const string devicePathStr = devicePath;
list <FilesystemPath> paths;
// APFS formatters may resolve /dev/rdiskN back to /dev/diskN internally.
AddUniqueMacOSXDevicePath (paths, devicePathStr);
AddUniqueMacOSXDevicePath (paths, GetMacOSXRawDevicePath (devicePathStr));
AddUniqueMacOSXDevicePath (paths, GetMacOSXBlockDevicePath (devicePathStr));
foreach (const FilesystemPath &path, paths)
{
try
{
File file;
file.Open (path, File::OpenReadWrite);
}
catch (...)
{
UserId origDeviceOwner = path.GetOwner();
// Register before chown so service-side success followed by
// an IPC failure can still be restored.
changedDeviceOwners.push_back (MacOSXFormatterDeviceOwnerRestore (path, origDeviceOwner));
Core->SetFileOwner (path, UserId (getuid()));
}
}
}
inline void RestoreMacOSXFormatterDeviceOwners (const MacOSXFormatterDeviceOwnerRestoreList &changedDeviceOwners)
{
foreach (const MacOSXFormatterDeviceOwnerRestore &restore, changedDeviceOwners)
{
try
{
Core->SetFileOwner (restore.Path, restore.Owner);
}
catch (...) { }
}
}
inline void ExecuteMacOSXFilesystemFormatter (const string &fsFormatter, const list <string> &args)
{
if (UseElevatedMacOSXAPFSFormatter (fsFormatter))
{
if (args.empty())
throw ParameterIncorrect (SRC_POS);
CoreService::RequestExecuteMacOSXAPFSFormatter (DevicePath (args.back()), getuid(), getgid());
return;
}
Process::Execute (IsMacOSXAPFSFormatter (fsFormatter) ? CoreService::GetMacOSXAPFSFormatterPath() : fsFormatter, args);
}
}
#endif // TC_MACOSX
#endif // VC_HEADER_Main_MacOSXFormatterDevice