1/* 2 * Copyright (c) 2012-2013 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 <uuid/uuid.h> 43#include <copyfile.h> 44 45#include "SecFileLocations.h" 46 47static CFURLRef sCustomHomeURL = NULL; 48 49static CFURLRef SecCopyHomeURL(void) 50{ 51 // This returns a CFURLRef so that it can be passed as the second parameter 52 // to CFURLCreateCopyAppendingPathComponent 53 54 CFURLRef homeURL = sCustomHomeURL; 55 if (homeURL) { 56 CFRetain(homeURL); 57 } else { 58#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) 59 // We would use CFCopyHomeDirectoryURL but it doesn't exist on MACOS. 60 // This does the same. 61 homeURL = CFCopyHomeDirectoryURLForUser(NULL); 62#else 63 homeURL = CFCopyHomeDirectoryURL(); 64#endif 65 } 66 67 return homeURL; 68} 69 70#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) 71static const char * get_host_uuid() 72{ 73 static uuid_string_t hostuuid = {}; 74 static dispatch_once_t onceToken; 75 dispatch_once(&onceToken, ^{ 76 struct timespec timeout = {30, 0}; 77 uuid_t uuid = {}; 78 if (gethostuuid(uuid, &timeout) == 0) { 79 uuid_unparse(uuid, hostuuid); 80 } else { 81 secerror("failed to get host uuid"); 82 } 83 }); 84 85 return hostuuid; 86} 87 88static CFStringRef copy_keychain_uuid_path(CFURLRef keyChainBaseURL) 89{ 90 CFStringRef baseURLString = NULL; 91 CFStringRef uuid_path = NULL; 92 93 require(keyChainBaseURL, done); 94 95 baseURLString = CFURLCopyFileSystemPath(keyChainBaseURL, kCFURLPOSIXPathStyle); 96 require(baseURLString, done); 97 98 uuid_path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%s"), baseURLString, get_host_uuid()); 99 100done: 101 CFReleaseSafe(baseURLString); 102 return uuid_path; 103} 104 105// See _kb_verify_create_path in securityd 106static bool keychain_verify_create_path(const char *keychainBasePath) 107{ 108 bool created = false; 109 struct stat st_info = {}; 110 char new_path[PATH_MAX] = {}; 111 char kb_path[PATH_MAX] = {}; 112 snprintf(kb_path, sizeof(kb_path), "%s", keychainBasePath); 113 if (lstat(kb_path, &st_info) == 0) { 114 if (S_ISDIR(st_info.st_mode)) { 115 created = true; 116 } else { 117 secerror("invalid directory at '%s' moving aside", kb_path); 118 snprintf(new_path, sizeof(new_path), "%s-invalid", kb_path); 119 unlink(new_path); 120 if (rename(kb_path, new_path) != 0) { 121 secerror("failed to rename file: %s (%s)", kb_path, strerror(errno)); 122 goto done; 123 } 124 } 125 } 126 if (!created) { 127 require_action(mkpath_np(kb_path, 0700) == 0, done, secerror("could not create path: %s (%s)", kb_path, strerror(errno))); 128 created = true; 129 } 130 131done: 132 return created; 133} 134#endif /*(TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ 135 136static CFURLRef SecCopyBaseFilesURL() 137{ 138 CFURLRef baseURL = sCustomHomeURL; 139 if (baseURL) { 140 CFRetain(baseURL); 141 } else { 142#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED)) 143 baseURL = SecCopyHomeURL(); 144#else 145 baseURL = CFURLCreateWithFileSystemPath(NULL, CFSTR("/"), kCFURLPOSIXPathStyle, true); 146#endif 147 } 148 return baseURL; 149} 150 151static CFURLRef SecCopyURLForFileInBaseDirectory(CFStringRef directoryPath, CFStringRef fileName) 152{ 153 CFURLRef fileURL = NULL; 154 CFStringRef suffix = NULL; 155 CFURLRef homeURL = SecCopyBaseFilesURL(); 156 157 if (fileName) 158 suffix = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%@"), directoryPath, fileName); 159 else 160 if (directoryPath) 161 suffix = CFStringCreateCopy(kCFAllocatorDefault, directoryPath); 162 163 if (homeURL && suffix) 164 fileURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, homeURL, suffix, false); 165 CFReleaseSafe(suffix); 166 CFReleaseSafe(homeURL); 167 return fileURL; 168} 169 170CFURLRef SecCopyURLForFileInKeychainDirectory(CFStringRef fileName) 171{ 172#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) 173 // need to tack on uuid here 174 Boolean isDirectory = (fileName == NULL); 175 CFURLRef resultURL = NULL; 176 CFStringRef resultStr = NULL; 177 __block bool directoryExists = false; 178 179 CFURLRef keyChainBaseURL = SecCopyURLForFileInBaseDirectory(CFSTR("Library/Keychains"), NULL); 180 CFStringRef uuid_path = copy_keychain_uuid_path(keyChainBaseURL); 181 CFStringPerformWithCString(uuid_path, ^(const char *utf8Str) { 182 directoryExists = keychain_verify_create_path(utf8Str); 183 }); 184 require(directoryExists, done); 185 if (fileName) 186 resultStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/%@"), uuid_path, fileName); 187 else 188 resultStr = CFStringCreateCopy(kCFAllocatorDefault, uuid_path); 189 190done: 191 CFReleaseSafe(uuid_path); 192 CFReleaseSafe(keyChainBaseURL); 193 if (resultStr) 194 { 195 resultURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, resultStr, kCFURLPOSIXPathStyle, isDirectory); 196 CFRelease(resultStr); 197 } 198 return resultURL; 199#else 200 return SecCopyURLForFileInBaseDirectory(CFSTR("Library/Keychains"), fileName); 201#endif 202} 203 204CFURLRef SecCopyURLForFileInPreferencesDirectory(CFStringRef fileName) 205{ 206 return SecCopyURLForFileInBaseDirectory(CFSTR("Library/Preferences"), fileName); 207} 208 209void WithPathInKeychainDirectory(CFStringRef fileName, void(^operation)(const char *utf8String)) 210{ 211 CFURLRef fileURL = SecCopyURLForFileInKeychainDirectory(fileName); 212 UInt8 buffer[MAXPATHLEN]; 213 CFURLGetFileSystemRepresentation(fileURL, false, buffer, sizeof(buffer)); 214 215 operation((const char*)buffer); 216 CFRelease(fileURL); 217} 218 219void SetCustomHomeURLString(CFStringRef home_path) 220{ 221 CFReleaseNull(sCustomHomeURL); 222 if (home_path) { 223 sCustomHomeURL = CFURLCreateWithFileSystemPath(NULL, home_path, kCFURLPOSIXPathStyle, true); 224 } 225} 226 227void SetCustomHomeURL(const char* path) 228{ 229 if (path) { 230 CFStringRef path_cf = CFStringCreateWithCStringNoCopy(NULL, path, kCFStringEncodingUTF8, kCFAllocatorNull); 231 SetCustomHomeURLString(path_cf); 232 CFReleaseSafe(path_cf); 233 } else { 234 SetCustomHomeURLString(NULL); 235 } 236} 237 238 239