]>
Commit | Line | Data |
---|---|---|
df0e469f A |
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 |