SecFileLocations.c
1 /* 2 * Copyright (c) 2012-2016 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 // 25 // SecFileLocations.c 26 // utilities 27 // 28 29 /* 30 This file incorporates code from securityd_files.c (iOS) and iOSforOSX.c (OSX). 31 */ 32 33 #include <TargetConditionals.h> 34 #include <AssertMacros.h> 35 #include <CoreFoundation/CFPriv.h> 36 #include <CoreFoundation/CFString.h> 37 #include <CoreFoundation/CFURL.h> 38 #include <CoreFoundation/CFUtilities.h> 39 #include <utilities/SecCFWrappers.h> 40 #include <utilities/SecCFRelease.h> 41 #include <sys/stat.h> 42 #include <sys/param.h> 43 #include <sys/errno.h> 44 #include <uuid/uuid.h> 45 #include <copyfile.h> 46 #include <syslog.h> 47 48 #include "SecFileLocations.h" 49 #include "OSX/sec/Security/SecKnownFilePaths.h" 50 51 52 #if TARGET_OS_OSX 53 static const char * get_host_uuid() 54 { 55 static uuid_string_t hostuuid = {}; 56 static dispatch_once_t onceToken; 57 dispatch_once(&onceToken, ^{ 58 struct timespec timeout = {30, 0}; 59 uuid_t uuid = {}; 60 if (gethostuuid(uuid, &timeout) == 0) { 61 uuid_unparse(uuid, hostuuid); 62 } else { 63 secerror("failed to get host uuid"); 64 } 65 }); 66 67 return hostuuid; 68 } 69 70 static CFStringRef copy_keychain_uuid_path(CFURLRef keyChainBaseURL) 71 { 72 CFStringRef baseURLString = NULL; 73 CFStringRef uuid_path = NULL; 74 75 require(keyChainBaseURL, done); 76 77 baseURLString = CFURLCopyFileSystemPath(keyChainBaseURL, kCFURLPOSIXPathStyle); 78 require(baseURLString, done); 79 80 uuid_path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%s"), baseURLString, get_host_uuid()); 81 82 done: 83 CFReleaseSafe(baseURLString); 84 return uuid_path; 85 } 86 87 // See _kb_verify_create_path in securityd 88 static bool keychain_verify_create_path(const char *keychainBasePath) 89 { 90 bool created = false; 91 struct stat st_info = {}; 92 char new_path[PATH_MAX] = {}; 93 char kb_path[PATH_MAX] = {}; 94 snprintf(kb_path, sizeof(kb_path), "%s", keychainBasePath); 95 if (lstat(kb_path, &st_info) == 0) { 96 if (S_ISDIR(st_info.st_mode)) { 97 created = true; 98 } else { 99 secerror("invalid directory at '%s' moving aside", kb_path); 100 snprintf(new_path, sizeof(new_path), "%s-invalid", kb_path); 101 unlink(new_path); 102 if (rename(kb_path, new_path) != 0) { 103 secerror("failed to rename file: %s (%s)", kb_path, strerror(errno)); 104 goto done; 105 } 106 } 107 } 108 if (!created) { 109 errno_t err = mkpath_np(kb_path, 0700); 110 require_action(err == 0 || err == EEXIST, done, secerror("could not create path: %s (%s)", kb_path, strerror(err))); 111 created = true; 112 } 113 114 done: 115 return created; 116 } 117 #endif /* TARGET_OS_OSX */ 118 119 CFURLRef SecCopyURLForFileInBaseDirectory(bool system, CFStringRef directoryPath, CFStringRef fileName) 120 { 121 CFURLRef fileURL = NULL; 122 CFStringRef suffix = NULL; 123 CFURLRef homeURL = SecCopyBaseFilesURL(system); 124 125 if (fileName) { 126 suffix = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%@"), directoryPath, fileName); 127 } else if (directoryPath) { 128 suffix = CFStringCreateCopy(kCFAllocatorDefault, directoryPath); 129 } 130 131 bool isDirectory = !fileName; 132 if (homeURL && suffix) { 133 fileURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, homeURL, suffix, isDirectory); 134 } 135 CFReleaseSafe(suffix); 136 CFReleaseSafe(homeURL); 137 return fileURL; 138 } 139 140 CFURLRef SecCopyURLForFileInKeychainDirectory(CFStringRef fileName) 141 { 142 #if TARGET_OS_OSX 143 // need to tack on uuid here 144 Boolean isDirectory = (fileName == NULL); 145 CFURLRef resultURL = NULL; 146 CFStringRef resultStr = NULL; 147 __block bool directoryExists = false; 148 149 CFURLRef keyChainBaseURL = SecCopyURLForFileInBaseDirectory(false, CFSTR("Library/Keychains"), NULL); 150 CFStringRef uuid_path = copy_keychain_uuid_path(keyChainBaseURL); 151 CFStringPerformWithCString(uuid_path, ^(const char *utf8Str) { 152 directoryExists = keychain_verify_create_path(utf8Str); 153 }); 154 require(directoryExists, done); 155 if (fileName) 156 resultStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%@"), uuid_path, fileName); 157 else 158 resultStr = CFStringCreateCopy(kCFAllocatorDefault, uuid_path); 159 160 done: 161 CFReleaseSafe(uuid_path); 162 CFReleaseSafe(keyChainBaseURL); 163 if (resultStr) 164 { 165 resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, resultStr, kCFURLPOSIXPathStyle, isDirectory); 166 CFRelease(resultStr); 167 } 168 return resultURL; 169 #else /* !TARGET_OS_OSX */ 170 return SecCopyURLForFileInBaseDirectory(true, CFSTR("Library/Keychains"), fileName); 171 #endif 172 } 173 174 CFURLRef SecCopyURLForFileInSystemKeychainDirectory(CFStringRef fileName) { 175 return SecCopyURLForFileInBaseDirectory(true, CFSTR("Library/Keychains"), fileName); 176 } 177 178 CFURLRef SecCopyURLForFileInUserCacheDirectory(CFStringRef fileName) 179 { 180 #if TARGET_OS_OSX 181 Boolean isDirectory = (fileName == NULL); 182 CFURLRef resultURL = NULL; 183 CFStringRef cacheDirStr = NULL; 184 char strBuffer[PATH_MAX + 1]; 185 size_t result = confstr(_CS_DARWIN_USER_CACHE_DIR, strBuffer, sizeof(strBuffer)); 186 if (result == 0) { 187 syslog(LOG_CRIT, "SecCopyURLForFileInUserCacheDirectory: confstr on _CS_DARWIN_USER_CACHE_DIR failed: %d", errno); 188 return resultURL; 189 } 190 cacheDirStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/%@"), strBuffer, fileName); 191 if (cacheDirStr) { 192 resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cacheDirStr, kCFURLPOSIXPathStyle, isDirectory); 193 } 194 CFReleaseSafe(cacheDirStr); 195 return resultURL; 196 #else 197 return SecCopyURLForFileInBaseDirectory(true, CFSTR("Library/Caches"), fileName); 198 #endif 199 } 200 201 CFURLRef SecCopyURLForFileInPreferencesDirectory(CFStringRef fileName) 202 { 203 return SecCopyURLForFileInBaseDirectory(false, CFSTR("Library/Preferences"), fileName); 204 } 205 206 CFURLRef SecCopyURLForFileInManagedPreferencesDirectory(CFStringRef fileName) 207 { 208 CFURLRef resultURL = NULL; 209 210 CFStringRef userName; 211 #if TARGET_OS_OSX 212 userName = CFCopyUserName(); 213 #else 214 userName = CFStringCreateWithCString(kCFAllocatorDefault, "mobile", kCFStringEncodingASCII); 215 #endif 216 217 if (userName) { 218 CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("/Library/Managed Preferences/%@/%@"), userName, fileName); 219 if (path) { 220 resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false); 221 CFReleaseSafe(path); 222 } 223 CFReleaseSafe(userName); 224 } 225 226 return resultURL; 227 } 228 229 CFURLRef SecCopyURLForFileInProtectedDirectory(CFStringRef fileName) 230 { 231 return SecCopyURLForFileInBaseDirectory(true, CFSTR("private/var/protected/"), fileName); 232 } 233 234 void WithPathInDirectory(CFURLRef fileURL, void(^operation)(const char *utf8String)) 235 { 236 /* Ownership of fileURL is taken by this function and so we release it. */ 237 if (fileURL) { 238 UInt8 buffer[PATH_MAX]; 239 CFURLGetFileSystemRepresentation(fileURL, false, buffer, sizeof(buffer)); 240 241 operation((const char*)buffer); 242 CFRelease(fileURL); 243 } 244 } 245 246 void WithPathInKeychainDirectory(CFStringRef fileName, void(^operation)(const char *utf8String)) 247 { 248 WithPathInDirectory(SecCopyURLForFileInKeychainDirectory(fileName), operation); 249 } 250 251 void WithPathInUserCacheDirectory(CFStringRef fileName, void(^operation)(const char *utf8String)) 252 { 253 WithPathInDirectory(SecCopyURLForFileInUserCacheDirectory(fileName), operation); 254 } 255 256 void WithPathInProtectedDirectory(CFStringRef fileName, void(^operation)(const char *utf8String)) 257 { 258 WithPathInDirectory(SecCopyURLForFileInProtectedDirectory(fileName), operation); 259 } 260 261 void SetCustomHomePath(const char* path) 262 { 263 if (path) { 264 CFStringRef path_cf = CFStringCreateWithCStringNoCopy(NULL, path, kCFStringEncodingUTF8, kCFAllocatorNull); 265 SecSetCustomHomeURLString(path_cf); 266 CFReleaseSafe(path_cf); 267 } else { 268 SecSetCustomHomeURLString(NULL); 269 } 270 } 271 272