]> git.saurik.com Git - apple/security.git/blob - Keychain/SFFileVault.cpp
Security-163.tar.gz
[apple/security.git] / Keychain / SFFileVault.cpp
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18 /*
19 * SFFileVault.cpp
20 * testFileVaultSPI
21 *
22 * Created by john on Mon Jul 14 2003.
23 *
24 */
25
26 //#include <iostream>
27
28 #include "SFFileVault.h"
29 #include "ExecCLITool.h"
30 #include <Security/SecKeychain.h>
31 #include <Security/KCCursor.h>
32 #include <Security/cfutilities.h>
33 #include <Security/Keychains.h>
34 #include <Security/KCUtilities.h>
35 #include <Security/globals.h>
36 #include <Security/Certificate.h>
37 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
38 #include <Security/SecKeychainAPIPriv.h>
39 #include "SecFileVaultCert.h"
40
41 #pragma mark -------------------- Environment Variables --------------------
42
43 #define HDIUTIL_PATH "/usr/bin/hdiutil" // environment var -> HDIUTIL_PATH
44 #define HDID_PATH "/usr/bin/hdid" // environment var -> HDID_PATH
45 #define MOUNT_HFS_PATH "/sbin/mount_hfs" // environment var -> MOUNT_HFS_PATH
46 #define UMOUNT_PATH "/sbin/umount" // environment var -> UMOUNT_PATH
47
48 // _defaultMasterKeychainPath // environment var -> FILEVAULT_MASTER_PATH
49
50 const char * const SFFileVault:: _defaultMasterKeychainPath = "/System/Library/Keychains/";
51 const char * const SFFileVault::_masterKeychainName = "FileVaultMaster";
52
53 #pragma mark -------------------- SFFileVault implementation --------------------
54
55 OSStatus SFFileVault::mount(CFStringRef password, CFURLRef certificate, CFURLRef dmgin,
56 CFURLRef mountpoint, CFStringRef *devicepath)
57 {
58 // /usr/bin/hdid -nomount -stdinpass -plist thevol.dmg
59 // /sbin/mount_hfs /dev/disk3s2 /tmp/THEVOL
60
61 const Boolean resolveAgainstBase = true;
62 char imageFileString[PATH_MAX + 1];
63 if (!CFURLGetFileSystemRepresentation(dmgin, resolveAgainstBase, reinterpret_cast<UInt8 *>(imageFileString), PATH_MAX))
64 MacOSError::throwMe(paramErr);
65
66 // @@@ Not implemented yet
67 if (certificate)
68 MacOSError::throwMe(unimpErr);
69
70 ExecCLITool rt;
71 rt.input(password,true); // include trailing NULL
72 rt.run(HDID_PATH,"HDID_PATH", "-nomount", "-stdinpass", "-plist", imageFileString, NULL);
73
74 CFRef<CFStringRef> devicePathString = extractDevicepath(rt); // parse stdout from hdid --> should be plist
75 if (!devicePathString)
76 MacOSError::throwMe(paramErr);
77 const char *devpath = CFStringGetCStringPtr(devicePathString, kCFStringEncodingMacRoman);
78 if (!devpath)
79 MacOSError::throwMe(ioErr);
80
81 char mountpointString[PATH_MAX + 1];
82 if (!CFURLGetFileSystemRepresentation(mountpoint, resolveAgainstBase, reinterpret_cast<UInt8 *>(mountpointString), PATH_MAX))
83 MacOSError::throwMe(paramErr);
84
85 rt.run(MOUNT_HFS_PATH,"MOUNT_HFS_PATH", devpath, mountpointString, NULL);
86 *devicepath = CFStringCreateCopy(NULL, devicePathString);
87
88 return noErr;
89 }
90
91 OSStatus SFFileVault::mastermount(CFURLRef dmgin, CFURLRef mountpoint, CFStringRef *devicepath)
92 {
93 // convenience call to call mount with master cert
94 CFStringRef password = NULL;
95 CFURLRef certificate = NULL;
96 getCertificate(&certificate);
97 OSStatus status = mount(password, certificate, dmgin, mountpoint, devicepath);
98 return status;
99 }
100
101 OSStatus SFFileVault::unmount(CFURLRef mountpoint, CFStringRef devicepath)
102 {
103 // To unmount, we do:
104 // /sbin/umount -f <mount point path> /sbin/umount -f /tmp/THEVOL
105 // /usr/bin/hdiutil detach <device path> /usr/bin/hdiutil detach /dev/disk3s2
106
107 ExecCLITool rt;
108
109 Boolean resolveAgainstBase = true;
110 char mountpointString[PATH_MAX + 1];
111 if (!CFURLGetFileSystemRepresentation(mountpoint, resolveAgainstBase, reinterpret_cast<UInt8 *>(mountpointString), PATH_MAX))
112 MacOSError::throwMe(paramErr);
113
114 // OSStatus status = rt.run(HDIUTIL_PATH,"HDIUTIL_PATH", "unmount", "-force", mtpt, NULL);
115 /* OSStatus status = */ rt.run(UMOUNT_PATH,"UMOUNT_PATH", "-f", mountpointString, NULL);
116
117 const char *devpath = CFStringGetCStringPtr(devicepath, kCFStringEncodingMacRoman);
118 if (!devpath)
119 MacOSError::throwMe(paramErr);
120
121 return rt.run(HDIUTIL_PATH,"HDIUTIL_PATH", "detach", devpath, NULL);
122 }
123
124 OSStatus SFFileVault::userChangePassword(CFStringRef password, CFStringRef devicepath)
125 {
126 // @@@ Not implemented yet, but code will be something like below
127 MacOSError::throwMe(unimpErr);
128
129 ExecCLITool rt;
130
131 const char *devpath = CFStringGetCStringPtr(devicepath, kCFStringEncodingMacRoman);
132 if (!devpath)
133 MacOSError::throwMe(paramErr);
134
135 rt.input(password,true); // include trailing NULL
136 return rt.run(HDIUTIL_PATH,"HDIUTIL_PATH", "chpass", devpath, NULL);
137
138 return noErr;
139 }
140
141 OSStatus SFFileVault::makeMasterPassword(CFStringRef masterPasswordPassword, SecKeychainRef *keychainRef)
142 {
143 /*
144 OSStatus SecFileVaultMakeMasterPassword(CFStringRef masterPasswordPassword);
145
146 *** In the real code, this will be done directly rather than exec'ing a tool, since there are too many parameters to specify
147 *** this needs to be done as root, since the keychain will be a system keychain
148 /usr/bin/certtool y c k=/System/Library/Keychains/FileVaultMaster.keychain p=<masterPasswordPassword>
149 /usr/bin/certtool c k=/System/Library/Keychains/FileVaultMaster.keychain o=/System/Library/Keychains/FileVaultMaster.cer
150 Two steps: create the keychain, then create the keypair
151 */
152
153 char masterKeychainPath[PATH_MAX + 1];
154 const char *envPath = getenv("FILEVAULT_MASTER_PATH"); // must set to full path or kc will end up in ~/Library/Keychains/
155 if (!envPath)
156 envPath = _defaultMasterKeychainPath;
157 snprintf(masterKeychainPath, sizeof(masterKeychainPath), "%s%s.keychain", envPath, _masterKeychainName);
158 // std::cout << "Masterkeychain path: " << masterKeychainPath << std::endl;
159
160 const char *mpass = CFStringGetCStringPtr(masterPasswordPassword, kCFStringEncodingMacRoman);
161 if (!mpass)
162 MacOSError::throwMe(paramErr);
163 const UInt32 passwordLength = strlen(mpass);
164
165 // don't add to searchlist
166 KeychainCore::Keychain keychain = KeychainCore::globals().storageManager.make(Required(&masterKeychainPath),false);
167
168 try
169 {
170 keychain->create(passwordLength, mpass);
171 }
172 catch (const MacOSError &err)
173 {
174 if (err.osStatus()!=errSecDuplicateKeychain)
175 throw;
176 }
177 catch (const CssmCommonError &err)
178 {
179 if (err.cssmError()!=CSSMERR_DL_DATASTORE_ALREADY_EXISTS)
180 throw;
181 }
182
183 RequiredParam(keychainRef)=keychain->handle();
184
185 // @@@ Need better identification for the certificate
186 SecFileVaultCert fvc;
187 CFStringRef hostName = CFSTR("com.apple.fv");
188 CFStringRef userName = CFSTR("User Name");
189 CFDataRef certData = NULL; //CFRef<>
190 OSStatus status = fvc.createPair(hostName,userName,*keychainRef,&certData);
191 if (status)
192 MacOSError::throwMe(status);
193 // Write out cert file
194 status = writeCertificateFile(certData);
195 if (status)
196 MacOSError::throwMe(status);
197
198 return noErr;
199 }
200
201 OSStatus SFFileVault::create(CFStringRef password, CFURLRef certificate, CFURLRef dmgout,
202 CFStringRef volumeName, CFStringRef sizeSpec)
203 {
204 // /usr/bin/hdiutil create -encryption -stdinpass -type SPARSE -fs "HFS+" -volname <vol name> -size 20g <path to disk image>
205
206 ExecCLITool rt;
207
208 // Construct the "-volname" parameter
209 if (!volumeName)
210 MacOSError::throwMe(paramErr);
211 const char *volname = CFStringGetCStringPtr(volumeName, kCFStringEncodingMacRoman);
212 if (!volname)
213 MacOSError::throwMe(paramErr);
214
215 // Construct the "-size" parameter
216 if (!sizeSpec)
217 MacOSError::throwMe(paramErr);
218 const char *sizestr = CFStringGetCStringPtr(sizeSpec, kCFStringEncodingMacRoman);
219 if (!sizestr)
220 MacOSError::throwMe(paramErr);
221
222 // Construct the file name parameter
223 CFRef<CFStringRef> fileString = CFURLCopyFileSystemPath(dmgout, kCFURLPOSIXPathStyle);
224 if (!fileString)
225 MacOSError::throwMe(paramErr);
226 const char *fname = CFStringGetCStringPtr(fileString, kCFStringEncodingMacRoman);
227 if (!fname)
228 MacOSError::throwMe(paramErr);
229
230 // Construct the "-certificate" parameter
231 const char *certificateParamString = certificate?"-certificate":"-layout"; // @@@ what is a safe empty param?
232 CFStringRef certificateFileString = certificate?CFURLCopyFileSystemPath(certificate, kCFURLPOSIXPathStyle):NULL;
233 if (certificate && !certificateFileString)
234 MacOSError::throwMe(paramErr);
235 const char *certFileString = certificate?CFStringGetCStringPtr(certificateFileString, kCFStringEncodingMacRoman):"SPUD";
236 if (certificate && !certFileString)
237 MacOSError::throwMe(paramErr);
238
239 rt.input(password,true); // include trailing NULL
240 OSStatus status = rt.run(HDIUTIL_PATH,"HDIUTIL_PATH", "create", "-encryption", "CEncryptedEncoding",
241 "-stdinpass", "-type", "SPARSE", "-fs", "HFS+", "-volname", volname, "-size", sizestr,
242 certificateParamString, certFileString, fname, NULL);
243
244 if (certificateFileString)
245 CFRelease(certificateFileString);
246
247 return status;
248 }
249
250 Boolean SFFileVault::masterPasswordEnabled(SecKeychainRef *keychainRef)
251 {
252 char masterKeychain[PATH_MAX + 1];
253 snprintf(masterKeychain, sizeof(masterKeychain), "%s.keychain", getKeychainPath()); //@@@ leak
254
255 SecKeychainRef tmpKeychainRef=KeychainCore::globals().storageManager.make(masterKeychain, false)->handle();
256 if (tmpKeychainRef == NULL)
257 return false;
258
259 if (keychainRef)
260 *keychainRef = tmpKeychainRef;
261 else
262 CFRelease(tmpKeychainRef);
263 return true;
264 }
265
266 OSStatus SFFileVault::changeMasterPasswordPassword(CFStringRef oldPassword,CFStringRef newPassword)
267 {
268 // Essentially SecKeychainChangePassword for the FileVault Master Password keychain
269 SecKeychainRef keychainRef;
270 if (!masterPasswordEnabled(&keychainRef))
271 MacOSError::throwMe(errSecNoSuchKeychain);
272
273 std::string oldpw = cfString(oldPassword); //UInt32
274 std::string newpw = cfString(newPassword);
275
276 KeychainCore::Keychain keychain = KeychainCore::Keychain::optional(keychainRef);
277 keychain->changePassphrase (oldpw.length(), oldpw.c_str(), newpw.length(), newpw.c_str());
278 CFRelease(keychainRef);
279 return noErr;
280 }
281
282 /*
283 Shouldn't cfString being using code like this?
284
285 const Boolean isExternalRepresentation = false;
286 const CFStringEncoding encoding = kCFStringEncodingUTF8;
287 CFIndex usedBufLen = 0;
288 UInt8 lossByte = 0;
289
290 if (!theString)
291 MacOSError::throwMe(paramErr);
292
293 CFRange stringRange = CFRangeMake(0,CFStringGetLength(theString));
294 // Call once first just to get length
295 CFIndex length = CFStringGetBytes(theString, stringRange, encoding, lossByte,
296 isExternalRepresentation, NULL, 0, &usedBufLen);
297 */
298
299 #pragma mark -------------------- Helpers --------------------
300
301 #define SYSTEM_ENTITIES_KEY CFSTR("system-entities")
302 #define CONTENT_HINT_KEY CFSTR("content-hint")
303 #define DEV_ENTRY_KEY CFSTR("dev-entry")
304 #define APPLE_HFS_KEY CFSTR("Apple_HFS")
305
306 CFStringRef SFFileVault::extractDevicepath(const ExecCLITool& rt)
307 {
308 CFRef<CFDataRef> tableData = CFDataCreate(NULL,reinterpret_cast<const UInt8 *>(rt.data()),rt.length());
309 CFStringRef errorString = NULL;
310 CFRef<CFDictionaryRef> devTable = static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(NULL,
311 tableData, kCFPropertyListImmutable, &errorString));
312 if (errorString != NULL)
313 {
314 CFRelease(errorString);
315 return NULL;
316 }
317
318 CFRef<CFArrayRef> sysEntities = static_cast<CFArrayRef>(CFDictionaryGetValue(devTable,SYSTEM_ENTITIES_KEY));
319 if (sysEntities == NULL)
320 return NULL;
321
322 CFIndex dictionaryCount = CFArrayGetCount(sysEntities);
323 for (CFIndex ix=0;ix < dictionaryCount;ix++)
324 {
325 CFRef<CFDictionaryRef> dict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(sysEntities, ix));
326 CFRef<CFStringRef> deviceEntryString = static_cast<CFStringRef>(CFDictionaryGetValue(dict,CONTENT_HINT_KEY));
327 if (CFEqual(deviceEntryString, APPLE_HFS_KEY)) // found it
328 return static_cast<CFStringRef>(CFDictionaryGetValue(dict,DEV_ENTRY_KEY));
329 }
330 return NULL;
331 }
332
333 OSStatus SFFileVault::getCertificate(CFURLRef *certificateFile)
334 {
335 //@@@ to be done
336 MacOSError::throwMe(unimpErr);
337 // do a find in the master keychain
338 char masterKeychain[PATH_MAX + 1];
339 snprintf(masterKeychain, sizeof(masterKeychain), "%s.keychain", getKeychainPath()); //@@@ leak
340
341 // don't add to searchlist
342 KeychainCore::Keychain keychain = KeychainCore::globals().storageManager.make(Required(&masterKeychain),false);
343 KeychainCore::StorageManager::KeychainList keychains;
344 KeychainCore::globals().storageManager.optionalSearchList(keychain, keychains);
345
346 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
347 KeychainCore::KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
348 KeychainCore::Item item;
349 if (!cursor->next(item))
350 CssmError::throwMe(errSecItemNotFound);
351
352 // KeychainCore::Certificate *certificate = static_cast<KeychainCore::Certificate *>(&*item);
353 // CSSM_DATA_PTR certData = static_cast<CSSM_DATA_PTR>(certificate->data());
354
355 return noErr;
356 }
357
358 OSStatus SFFileVault::writeCertificateFile(CFDataRef certData)
359 {
360 const char *certFile = getCertificateFileName();
361 OSStatus status = writeFile(certFile, CFDataGetBytePtr(certData), CFDataGetLength(certData));
362 if (certFile)
363 ::free(const_cast<char *>(certFile));
364 return status;
365 }
366
367 const char *SFFileVault::getKeychainPath()
368 {
369 // Append ".keychain to get keychain name; append .cer to get certificate
370 char masterKeychainPath[PATH_MAX + 1];
371 const char *envPath = getenv("FILEVAULT_MASTER_PATH"); // must set to full path or kc will end up in ~/Library/Keychains/
372 if (!envPath)
373 envPath = _defaultMasterKeychainPath;
374 snprintf(masterKeychainPath, sizeof(masterKeychainPath), "%s%s", envPath, _masterKeychainName);
375 // std::cout << "Masterkeychain path: " << masterKeychainPath << std::endl;
376 size_t sz = strlen(masterKeychainPath)+1;
377 char *path = static_cast<char *>(malloc(sz));
378 strncpy(path,masterKeychainPath,sz);
379 return static_cast<const char *>(path);
380 }
381
382 const char *SFFileVault::getCertificateFileName()
383 {
384 char certFile[PATH_MAX + 1];
385 snprintf(certFile, sizeof(certFile), "%s.cer", getKeychainPath());
386 size_t sz = strlen(certFile)+1;
387 char *path = static_cast<char *>(malloc(sz));
388 strncpy(path,certFile,sz);
389 return static_cast<const char *>(path);
390 }
391
392 int SFFileVault::writeFile(const char *fileName, const unsigned char *bytes, unsigned int numBytes)
393 {
394 int fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
395 if (fd <= 0)
396 return errno;
397
398 if (lseek(fd, 0, SEEK_SET) < 0)
399 return errno;
400
401 int rtn = write(fd, bytes, (size_t)numBytes);
402 rtn = (rtn != static_cast<int>(numBytes))?EIO:0;
403 close(fd);
404 return rtn;
405 }
406
407 #pragma mark -------------------- Unused --------------------
408