diff --git a/src/Core/Unix/MacOSX/CoreMacOSX.cpp b/src/Core/Unix/MacOSX/CoreMacOSX.cpp index a422b338..440e2cd4 100644 --- a/src/Core/Unix/MacOSX/CoreMacOSX.cpp +++ b/src/Core/Unix/MacOSX/CoreMacOSX.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,73 +32,66 @@ namespace VeraCrypt { - static string DecodePlistXmlString (const string &xmlString) + // RAII wrapper so CFRelease is never forgotten on early returns / exceptions. + class CFHolder { - string decoded; + public: + explicit CFHolder (CFTypeRef ref = nullptr) : Ref (ref) { } + ~CFHolder () { if (Ref) CFRelease (Ref); } + CFTypeRef Get () const { return Ref; } + private: + CFHolder (const CFHolder &); + CFHolder &operator= (const CFHolder &); + CFTypeRef Ref; + }; - for (size_t i = 0; i < xmlString.size(); ++i) - { - if (xmlString[i] != '&') - { - decoded += xmlString[i]; - continue; - } + static string CFStringToStdString (CFStringRef cfStr) + { + if (!cfStr) + return string(); - if (xmlString.compare (i, 5, "&") == 0) - { - decoded += '&'; - i += 4; - } - else if (xmlString.compare (i, 4, "<") == 0) - { - decoded += '<'; - i += 3; - } - else if (xmlString.compare (i, 4, ">") == 0) - { - decoded += '>'; - i += 3; - } - else if (xmlString.compare (i, 6, """) == 0) - { - decoded += '"'; - i += 5; - } - else if (xmlString.compare (i, 6, "'") == 0) - { - decoded += '\''; - i += 5; - } - else - decoded += xmlString[i]; - } + CFIndex length = CFStringGetLength (cfStr); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding (length, kCFStringEncodingUTF8) + 1; + if (maxSize <= 0) + return string(); - return decoded; + vector buffer ((size_t) maxSize); + if (!CFStringGetCString (cfStr, &buffer[0], maxSize, kCFStringEncodingUTF8)) + return string(); + + return string (&buffer[0]); } - static bool ExtractPlistString (const string &xml, const string &key, size_t start, size_t limit, string &value, size_t *endPos = nullptr) + // Fetch a string value from a CFDictionary by (C-string) key; empty if absent + // or not a string. + static string CFDictionaryGetStdString (CFDictionaryRef dict, const char *key) { - // hdiutil currently emits simple namevalue pairs. - // This lightweight parser assumes the value follows the requested key. - string keyTag = "" + key + ""; - size_t p = xml.find (keyTag, start); - if (p == string::npos || p >= limit) - return false; + if (!dict) + return string(); - p = xml.find ("", p + keyTag.size()); - if (p == string::npos || p >= limit) - return false; - p += 8; + CFHolder cfKey (CFStringCreateWithCString (kCFAllocatorDefault, key, kCFStringEncodingUTF8)); + if (!cfKey.Get()) + return string(); - size_t e = xml.find ("", p); - if (e == string::npos || e > limit) - return false; + CFTypeRef value = CFDictionaryGetValue (dict, cfKey.Get()); // borrowed reference + if (!value || CFGetTypeID (value) != CFStringGetTypeID()) + return string(); - value = DecodePlistXmlString (xml.substr (p, e - p)); - if (endPos) - *endPos = e + 9; + return CFStringToStdString ((CFStringRef) value); + } - return true; + // Parse an hdiutil -plist (XML) document into a CFPropertyList. Returns nullptr + // on failure; the caller owns the result and must CFRelease it. + static CFPropertyListRef ParsePropertyList (const string &xml) + { + if (xml.empty()) + return nullptr; + + CFHolder data (CFDataCreate (kCFAllocatorDefault, (const UInt8 *) xml.data(), (CFIndex) xml.size())); + if (!data.Get()) + return nullptr; + + return CFPropertyListCreateWithData (kCFAllocatorDefault, (CFDataRef) data.Get(), kCFPropertyListImmutable, nullptr, nullptr); } static string NormalizeDiskImagePath (const string &path) @@ -125,45 +120,40 @@ namespace VeraCrypt return normalized; } - static bool ExtractDiskImageDeviceAndMountPoint (const string &xml, size_t start, size_t limit, DevicePath &device, DirectoryPath &mountPoint) + // Walk a "system-entities" array (from hdiutil attach/info). Prefer the entity + // that carries a mount-point; otherwise fall back to the first dev-entry. + static bool ExtractDeviceAndMountPointFromEntities (CFArrayRef entities, DevicePath &device, DirectoryPath &mountPoint) { + if (!entities || CFGetTypeID (entities) != CFArrayGetTypeID()) + return false; + string firstDevice; string mountedDevice; string mountedPath; - for (size_t p = start; ; ) + CFIndex count = CFArrayGetCount (entities); + for (CFIndex i = 0; i < count; ++i) { - size_t devKeyPos = xml.find ("dev-entry", p); - if (devKeyPos == string::npos || devKeyPos >= limit) - break; - - string devEntry; - size_t devValueEnd = 0; - if (!ExtractPlistString (xml, "dev-entry", devKeyPos, limit, devEntry, &devValueEnd)) - { - p = devKeyPos + 1; + CFTypeRef entry = CFArrayGetValueAtIndex (entities, i); // borrowed reference + if (!entry || CFGetTypeID (entry) != CFDictionaryGetTypeID()) + continue; + + CFDictionaryRef entryDict = (CFDictionaryRef) entry; + + string devEntry = StringConverter::Trim (CFDictionaryGetStdString (entryDict, "dev-entry")); + if (devEntry.empty()) continue; - } - devEntry = StringConverter::Trim (devEntry); if (firstDevice.empty()) firstDevice = devEntry; - size_t nextDevKeyPos = xml.find ("dev-entry", devValueEnd); - if (nextDevKeyPos == string::npos || nextDevKeyPos > limit) - nextDevKeyPos = limit; - - string currentMountPoint; - // hdiutil currently emits dev-entry before mount-point inside each entity. - if (ExtractPlistString (xml, "mount-point", devValueEnd, nextDevKeyPos, currentMountPoint) - && !currentMountPoint.empty()) + string currentMountPoint = CFDictionaryGetStdString (entryDict, "mount-point"); + if (!currentMountPoint.empty()) { mountedDevice = devEntry; mountedPath = currentMountPoint; break; } - - p = devValueEnd; } if (!mountedDevice.empty()) @@ -182,6 +172,20 @@ namespace VeraCrypt return false; } + // Parse "hdiutil attach -plist" output (top-level dict with "system-entities"). + static bool ExtractDiskImageDeviceAndMountPoint (const string &attachXml, DevicePath &device, DirectoryPath &mountPoint) + { + CFHolder plist (ParsePropertyList (attachXml)); + if (!plist.Get() || CFGetTypeID (plist.Get()) != CFDictionaryGetTypeID()) + return false; + + CFTypeRef entities = CFDictionaryGetValue ((CFDictionaryRef) plist.Get(), CFSTR ("system-entities")); // borrowed + if (!entities || CFGetTypeID (entities) != CFArrayGetTypeID()) + return false; + + return ExtractDeviceAndMountPointFromEntities ((CFArrayRef) entities, device, mountPoint); + } + static bool FindDiskImageInfoByImagePath (const string &imagePath, DevicePath &device, DirectoryPath &mountPoint) { list args; @@ -191,27 +195,35 @@ namespace VeraCrypt string xml = Process::Execute ("/usr/bin/hdiutil", args); string normalizedImagePath = NormalizeDiskImagePath (imagePath); - for (size_t p = 0; ; ) + CFHolder plist (ParsePropertyList (xml)); + if (!plist.Get() || CFGetTypeID (plist.Get()) != CFDictionaryGetTypeID()) + return false; + + CFTypeRef images = CFDictionaryGetValue ((CFDictionaryRef) plist.Get(), CFSTR ("images")); // borrowed + if (!images || CFGetTypeID (images) != CFArrayGetTypeID()) + return false; + + CFArrayRef imageArray = (CFArrayRef) images; + CFIndex count = CFArrayGetCount (imageArray); + for (CFIndex i = 0; i < count; ++i) { - size_t imageKeyPos = xml.find ("image-path", p); - if (imageKeyPos == string::npos) - break; - - string currentImagePath; - size_t imageValueEnd = 0; - if (!ExtractPlistString (xml, "image-path", imageKeyPos, string::npos, currentImagePath, &imageValueEnd)) - { - p = imageKeyPos + 1; + CFTypeRef image = CFArrayGetValueAtIndex (imageArray, i); // borrowed + if (!image || CFGetTypeID (image) != CFDictionaryGetTypeID()) continue; - } - size_t nextImageKeyPos = xml.find ("image-path", imageValueEnd); - if (NormalizeDiskImagePath (currentImagePath) == normalizedImagePath) - { - return ExtractDiskImageDeviceAndMountPoint (xml, imageValueEnd, nextImageKeyPos, device, mountPoint); - } + CFDictionaryRef imageDict = (CFDictionaryRef) image; - p = imageValueEnd; + string currentImagePath = CFDictionaryGetStdString (imageDict, "image-path"); + if (NormalizeDiskImagePath (currentImagePath) != normalizedImagePath) + continue; + + // Matching image found: extract from its system-entities (mirrors the + // previous behavior of returning the result for the first match). + CFTypeRef entities = CFDictionaryGetValue (imageDict, CFSTR ("system-entities")); // borrowed + if (!entities || CFGetTypeID (entities) != CFArrayGetTypeID()) + return false; + + return ExtractDeviceAndMountPointFromEntities ((CFArrayRef) entities, device, mountPoint); } return false; @@ -473,7 +485,7 @@ namespace VeraCrypt DevicePath virtualDev; DirectoryPath mountPoint; - if (!ExtractDiskImageDeviceAndMountPoint (xml, 0, string::npos, virtualDev, mountPoint) + if (!ExtractDiskImageDeviceAndMountPoint (xml, virtualDev, mountPoint) || virtualDev.IsEmpty()) throw ParameterIncorrect (SRC_POS); (void) mountPoint;