2 * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved.
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
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.
22 * Created by john on Mon Jul 14 2003.
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"
41 #pragma mark -------------------- Environment Variables --------------------
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
48 // _defaultMasterKeychainPath // environment var -> FILEVAULT_MASTER_PATH
50 const char * const SFFileVault:: _defaultMasterKeychainPath
= "/System/Library/Keychains/";
51 const char * const SFFileVault::_masterKeychainName
= "FileVaultMaster";
53 #pragma mark -------------------- SFFileVault implementation --------------------
55 OSStatus
SFFileVault::mount(CFStringRef password
, CFURLRef certificate
, CFURLRef dmgin
,
56 CFURLRef mountpoint
, CFStringRef
*devicepath
)
58 // /usr/bin/hdid -nomount -stdinpass -plist thevol.dmg
59 // /sbin/mount_hfs /dev/disk3s2 /tmp/THEVOL
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
);
66 // @@@ Not implemented yet
68 MacOSError::throwMe(unimpErr
);
71 rt
.input(password
,true); // include trailing NULL
72 rt
.run(HDID_PATH
,"HDID_PATH", "-nomount", "-stdinpass", "-plist", imageFileString
, NULL
);
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
);
79 MacOSError::throwMe(ioErr
);
81 char mountpointString
[PATH_MAX
+ 1];
82 if (!CFURLGetFileSystemRepresentation(mountpoint
, resolveAgainstBase
, reinterpret_cast<UInt8
*>(mountpointString
), PATH_MAX
))
83 MacOSError::throwMe(paramErr
);
85 rt
.run(MOUNT_HFS_PATH
,"MOUNT_HFS_PATH", devpath
, mountpointString
, NULL
);
86 *devicepath
= CFStringCreateCopy(NULL
, devicePathString
);
91 OSStatus
SFFileVault::mastermount(CFURLRef dmgin
, CFURLRef mountpoint
, CFStringRef
*devicepath
)
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
);
101 OSStatus
SFFileVault::unmount(CFURLRef mountpoint
, CFStringRef devicepath
)
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
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
);
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
);
117 const char *devpath
= CFStringGetCStringPtr(devicepath
, kCFStringEncodingMacRoman
);
119 MacOSError::throwMe(paramErr
);
121 return rt
.run(HDIUTIL_PATH
,"HDIUTIL_PATH", "detach", devpath
, NULL
);
124 OSStatus
SFFileVault::userChangePassword(CFStringRef password
, CFStringRef devicepath
)
126 // @@@ Not implemented yet, but code will be something like below
127 MacOSError::throwMe(unimpErr
);
131 const char *devpath
= CFStringGetCStringPtr(devicepath
, kCFStringEncodingMacRoman
);
133 MacOSError::throwMe(paramErr
);
135 rt
.input(password
,true); // include trailing NULL
136 return rt
.run(HDIUTIL_PATH
,"HDIUTIL_PATH", "chpass", devpath
, NULL
);
141 OSStatus
SFFileVault::makeMasterPassword(CFStringRef masterPasswordPassword
, SecKeychainRef
*keychainRef
)
144 OSStatus SecFileVaultMakeMasterPassword(CFStringRef masterPasswordPassword);
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
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/
156 envPath
= _defaultMasterKeychainPath
;
157 snprintf(masterKeychainPath
, sizeof(masterKeychainPath
), "%s%s.keychain", envPath
, _masterKeychainName
);
158 // std::cout << "Masterkeychain path: " << masterKeychainPath << std::endl;
160 const char *mpass
= CFStringGetCStringPtr(masterPasswordPassword
, kCFStringEncodingMacRoman
);
162 MacOSError::throwMe(paramErr
);
163 const UInt32 passwordLength
= strlen(mpass
);
165 // don't add to searchlist
166 KeychainCore::Keychain keychain
= KeychainCore::globals().storageManager
.make(Required(&masterKeychainPath
),false);
170 keychain
->create(passwordLength
, mpass
);
172 catch (const MacOSError
&err
)
174 if (err
.osStatus()!=errSecDuplicateKeychain
)
177 catch (const CssmCommonError
&err
)
179 if (err
.cssmError()!=CSSMERR_DL_DATASTORE_ALREADY_EXISTS
)
183 RequiredParam(keychainRef
)=keychain
->handle();
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
);
192 MacOSError::throwMe(status
);
193 // Write out cert file
194 status
= writeCertificateFile(certData
);
196 MacOSError::throwMe(status
);
201 OSStatus
SFFileVault::create(CFStringRef password
, CFURLRef certificate
, CFURLRef dmgout
,
202 CFStringRef volumeName
, CFStringRef sizeSpec
)
204 // /usr/bin/hdiutil create -encryption -stdinpass -type SPARSE -fs "HFS+" -volname <vol name> -size 20g <path to disk image>
208 // Construct the "-volname" parameter
210 MacOSError::throwMe(paramErr
);
211 const char *volname
= CFStringGetCStringPtr(volumeName
, kCFStringEncodingMacRoman
);
213 MacOSError::throwMe(paramErr
);
215 // Construct the "-size" parameter
217 MacOSError::throwMe(paramErr
);
218 const char *sizestr
= CFStringGetCStringPtr(sizeSpec
, kCFStringEncodingMacRoman
);
220 MacOSError::throwMe(paramErr
);
222 // Construct the file name parameter
223 CFRef
<CFStringRef
> fileString
= CFURLCopyFileSystemPath(dmgout
, kCFURLPOSIXPathStyle
);
225 MacOSError::throwMe(paramErr
);
226 const char *fname
= CFStringGetCStringPtr(fileString
, kCFStringEncodingMacRoman
);
228 MacOSError::throwMe(paramErr
);
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
);
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
);
244 if (certificateFileString
)
245 CFRelease(certificateFileString
);
250 Boolean
SFFileVault::masterPasswordEnabled(SecKeychainRef
*keychainRef
)
252 char masterKeychain
[PATH_MAX
+ 1];
253 snprintf(masterKeychain
, sizeof(masterKeychain
), "%s.keychain", getKeychainPath()); //@@@ leak
255 SecKeychainRef tmpKeychainRef
=KeychainCore::globals().storageManager
.make(masterKeychain
, false)->handle();
256 if (tmpKeychainRef
== NULL
)
260 *keychainRef
= tmpKeychainRef
;
262 CFRelease(tmpKeychainRef
);
266 OSStatus
SFFileVault::changeMasterPasswordPassword(CFStringRef oldPassword
,CFStringRef newPassword
)
268 // Essentially SecKeychainChangePassword for the FileVault Master Password keychain
269 SecKeychainRef keychainRef
;
270 if (!masterPasswordEnabled(&keychainRef
))
271 MacOSError::throwMe(errSecNoSuchKeychain
);
273 std::string oldpw
= cfString(oldPassword
); //UInt32
274 std::string newpw
= cfString(newPassword
);
276 KeychainCore::Keychain keychain
= KeychainCore::Keychain::optional(keychainRef
);
277 keychain
->changePassphrase (oldpw
.length(), oldpw
.c_str(), newpw
.length(), newpw
.c_str());
278 CFRelease(keychainRef
);
283 Shouldn't cfString being using code like this?
285 const Boolean isExternalRepresentation = false;
286 const CFStringEncoding encoding = kCFStringEncodingUTF8;
287 CFIndex usedBufLen = 0;
291 MacOSError::throwMe(paramErr);
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);
299 #pragma mark -------------------- Helpers --------------------
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")
306 CFStringRef
SFFileVault::extractDevicepath(const ExecCLITool
& rt
)
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
)
314 CFRelease(errorString
);
318 CFRef
<CFArrayRef
> sysEntities
= static_cast<CFArrayRef
>(CFDictionaryGetValue(devTable
,SYSTEM_ENTITIES_KEY
));
319 if (sysEntities
== NULL
)
322 CFIndex dictionaryCount
= CFArrayGetCount(sysEntities
);
323 for (CFIndex ix
=0;ix
< dictionaryCount
;ix
++)
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
));
333 OSStatus
SFFileVault::getCertificate(CFURLRef
*certificateFile
)
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
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
);
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
);
352 // KeychainCore::Certificate *certificate = static_cast<KeychainCore::Certificate *>(&*item);
353 // CSSM_DATA_PTR certData = static_cast<CSSM_DATA_PTR>(certificate->data());
358 OSStatus
SFFileVault::writeCertificateFile(CFDataRef certData
)
360 const char *certFile
= getCertificateFileName();
361 OSStatus status
= writeFile(certFile
, CFDataGetBytePtr(certData
), CFDataGetLength(certData
));
363 ::free(const_cast<char *>(certFile
));
367 const char *SFFileVault::getKeychainPath()
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/
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
);
382 const char *SFFileVault::getCertificateFileName()
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
);
392 int SFFileVault::writeFile(const char *fileName
, const unsigned char *bytes
, unsigned int numBytes
)
394 int fd
= open(fileName
, O_RDWR
| O_CREAT
| O_TRUNC
, 0600);
398 if (lseek(fd
, 0, SEEK_SET
) < 0)
401 int rtn
= write(fd
, bytes
, (size_t)numBytes
);
402 rtn
= (rtn
!= static_cast<int>(numBytes
))?EIO
:0;
407 #pragma mark -------------------- Unused --------------------