/ OSX / utilities / SecFileLocations.c
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