SecFileLocations.c [plain text]
#include <TargetConditionals.h>
#include <AssertMacros.h>
#include <CoreFoundation/CFPriv.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFURL.h>
#include <CoreFoundation/CFUtilities.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCFRelease.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <uuid/uuid.h>
#include <copyfile.h>
#include <syslog.h>
#include "SecFileLocations.h"
static CFURLRef sCustomHomeURL = NULL;
CFURLRef SecCopyHomeURL(void)
{
CFURLRef homeURL = sCustomHomeURL;
if (homeURL) {
CFRetain(homeURL);
} else {
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
homeURL = CFCopyHomeDirectoryURLForUser(NULL);
#else
homeURL = CFCopyHomeDirectoryURL();
#endif
}
return homeURL;
}
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
static const char * get_host_uuid()
{
static uuid_string_t hostuuid = {};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
struct timespec timeout = {30, 0};
uuid_t uuid = {};
if (gethostuuid(uuid, &timeout) == 0) {
uuid_unparse(uuid, hostuuid);
} else {
secerror("failed to get host uuid");
}
});
return hostuuid;
}
static CFStringRef copy_keychain_uuid_path(CFURLRef keyChainBaseURL)
{
CFStringRef baseURLString = NULL;
CFStringRef uuid_path = NULL;
require(keyChainBaseURL, done);
baseURLString = CFURLCopyFileSystemPath(keyChainBaseURL, kCFURLPOSIXPathStyle);
require(baseURLString, done);
uuid_path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%s"), baseURLString, get_host_uuid());
done:
CFReleaseSafe(baseURLString);
return uuid_path;
}
static bool keychain_verify_create_path(const char *keychainBasePath)
{
bool created = false;
struct stat st_info = {};
char new_path[PATH_MAX] = {};
char kb_path[PATH_MAX] = {};
snprintf(kb_path, sizeof(kb_path), "%s", keychainBasePath);
if (lstat(kb_path, &st_info) == 0) {
if (S_ISDIR(st_info.st_mode)) {
created = true;
} else {
secerror("invalid directory at '%s' moving aside", kb_path);
snprintf(new_path, sizeof(new_path), "%s-invalid", kb_path);
unlink(new_path);
if (rename(kb_path, new_path) != 0) {
secerror("failed to rename file: %s (%s)", kb_path, strerror(errno));
goto done;
}
}
}
if (!created) {
require_action(mkpath_np(kb_path, 0700) == 0, done, secerror("could not create path: %s (%s)", kb_path, strerror(errno)));
created = true;
}
done:
return created;
}
#endif
static CFURLRef SecCopyBaseFilesURL()
{
CFURLRef baseURL = sCustomHomeURL;
if (baseURL) {
CFRetain(baseURL);
} else {
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED))
baseURL = SecCopyHomeURL();
#else
baseURL = CFURLCreateWithFileSystemPath(NULL, CFSTR("/"), kCFURLPOSIXPathStyle, true);
#endif
}
return baseURL;
}
static CFURLRef SecCopyURLForFileInBaseDirectory(CFStringRef directoryPath, CFStringRef fileName)
{
CFURLRef fileURL = NULL;
CFStringRef suffix = NULL;
CFURLRef homeURL = SecCopyBaseFilesURL();
if (fileName)
suffix = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%@"), directoryPath, fileName);
else
if (directoryPath)
suffix = CFStringCreateCopy(kCFAllocatorDefault, directoryPath);
if (homeURL && suffix)
fileURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, homeURL, suffix, false);
CFReleaseSafe(suffix);
CFReleaseSafe(homeURL);
return fileURL;
}
CFURLRef SecCopyURLForFileInKeychainDirectory(CFStringRef fileName)
{
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
Boolean isDirectory = (fileName == NULL);
CFURLRef resultURL = NULL;
CFStringRef resultStr = NULL;
__block bool directoryExists = false;
CFURLRef keyChainBaseURL = SecCopyURLForFileInBaseDirectory(CFSTR("Library/Keychains"), NULL);
CFStringRef uuid_path = copy_keychain_uuid_path(keyChainBaseURL);
CFStringPerformWithCString(uuid_path, ^(const char *utf8Str) {
directoryExists = keychain_verify_create_path(utf8Str);
});
require(directoryExists, done);
if (fileName)
resultStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%@"), uuid_path, fileName);
else
resultStr = CFStringCreateCopy(kCFAllocatorDefault, uuid_path);
done:
CFReleaseSafe(uuid_path);
CFReleaseSafe(keyChainBaseURL);
if (resultStr)
{
resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, resultStr, kCFURLPOSIXPathStyle, isDirectory);
CFRelease(resultStr);
}
return resultURL;
#else
return SecCopyURLForFileInBaseDirectory(CFSTR("Library/Keychains"), fileName);
#endif
}
CFURLRef SecCopyURLForFileInUserCacheDirectory(CFStringRef fileName)
{
#if TARGET_OS_OSX
Boolean isDirectory = (fileName == NULL);
CFURLRef resultURL = NULL;
CFStringRef cacheDirStr = NULL;
char strBuffer[PATH_MAX + 1];
size_t result = confstr(_CS_DARWIN_USER_CACHE_DIR, strBuffer, sizeof(strBuffer));
if (result == 0) {
syslog(LOG_CRIT, "SecCopyURLForFileInUserCacheDirectory: confstr on _CS_DARWIN_USER_CACHE_DIR failed: %d", errno);
return resultURL;
}
cacheDirStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/%@"), strBuffer, fileName);
if (cacheDirStr) {
resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cacheDirStr, kCFURLPOSIXPathStyle, isDirectory);
}
CFReleaseSafe(cacheDirStr);
return resultURL;
#else
return SecCopyURLForFileInBaseDirectory(CFSTR("Library/Caches"), fileName);
#endif
}
CFURLRef SecCopyURLForFileInPreferencesDirectory(CFStringRef fileName)
{
return SecCopyURLForFileInBaseDirectory(CFSTR("Library/Preferences"), fileName);
}
CFURLRef SecCopyURLForFileInManagedPreferencesDirectory(CFStringRef fileName)
{
CFURLRef resultURL = NULL;
CFStringRef userName;
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
userName = CFCopyUserName();
#else
userName = CFStringCreateWithCString(kCFAllocatorDefault, "mobile", kCFStringEncodingASCII);
#endif
if (userName) {
CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("/Library/Managed Preferences/%@/%@"), userName, fileName);
if (path) {
resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false);
CFReleaseSafe(path);
}
CFReleaseSafe(userName);
}
return resultURL;
}
CFURLRef SecCopyURLForFileInRevocationInfoDirectory(CFStringRef fileName)
{
CFURLRef resultURL = NULL;
CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("/Library/Keychains/crls/%@"), fileName);
if (path) {
resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false);
CFReleaseSafe(path);
}
return resultURL;
}
static void WithPathInDirectory(CFURLRef fileURL, void(^operation)(const char *utf8String))
{
if (fileURL) {
UInt8 buffer[PATH_MAX];
CFURLGetFileSystemRepresentation(fileURL, false, buffer, sizeof(buffer));
operation((const char*)buffer);
CFRelease(fileURL);
}
}
void WithPathInRevocationInfoDirectory(CFStringRef fileName, void(^operation)(const char *utf8String))
{
WithPathInDirectory(SecCopyURLForFileInRevocationInfoDirectory(fileName), operation);
}
void WithPathInKeychainDirectory(CFStringRef fileName, void(^operation)(const char *utf8String))
{
WithPathInDirectory(SecCopyURLForFileInKeychainDirectory(fileName), operation);
}
void WithPathInUserCacheDirectory(CFStringRef fileName, void(^operation)(const char *utf8String))
{
WithPathInDirectory(SecCopyURLForFileInUserCacheDirectory(fileName), operation);
}
void SetCustomHomeURL(CFURLRef url)
{
sCustomHomeURL = CFRetainSafe(url);
}
void SetCustomHomeURLString(CFStringRef home_path)
{
CFReleaseNull(sCustomHomeURL);
if (home_path) {
sCustomHomeURL = CFURLCreateWithFileSystemPath(NULL, home_path, kCFURLPOSIXPathStyle, true);
}
}
void SetCustomHomePath(const char* path)
{
if (path) {
CFStringRef path_cf = CFStringCreateWithCStringNoCopy(NULL, path, kCFStringEncodingUTF8, kCFAllocatorNull);
SetCustomHomeURLString(path_cf);
CFReleaseSafe(path_cf);
} else {
SetCustomHomeURLString(NULL);
}
}