1/* 2 * Copyright (c) 2006-2012 Apple Inc. All rights reserved. 3 * 4 * 5 * @APPLE_LICENSE_HEADER_START@ 6 * 7 * This file contains Original Code and/or Modifications of Original Code 8 * as defined in and that are subject to the Apple Public Source License 9 * Version 2.0 (the 'License'). You may not use this file except in 10 * compliance with the License. Please obtain a copy of the License at 11 * http://www.opensource.apple.com/apsl/ and read it before using this 12 * file. 13 * 14 * The Original Code and all software distributed under the License are 15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 * Please see the License for the specific language governing rights and 20 * limitations under the License. 21 * 22 * @APPLE_LICENSE_HEADER_END@ 23 */ 24/* 25 * FILE: bootcaches.c 26 * AUTH: Soren Spies (sspies) 27 * DATE: "spring" 2006 28 * DESC: routines for bootcache data 29 * 30 */ 31 32#include <bless.h> 33#include <bootfiles.h> 34#include <IOKit/IOKitLib.h> 35 36#include <fcntl.h> 37#include <libgen.h> 38#include <notify.h> 39#include <paths.h> 40#include <mach/mach.h> 41#include <mach/kmod.h> 42#include <sys/attr.h> 43#include <sys/stat.h> 44#include <sys/sysctl.h> 45#include <sys/time.h> 46#include <sys/types.h> 47#include <unistd.h> 48 49#include <EFILogin/EFILogin.h> 50#include <System/libkern/mkext.h> 51#include <System/libkern/OSKextLibPrivate.h> 52#include <DiskArbitration/DiskArbitration.h> // for UUID fetching 53#include <IOKit/kext/fat_util.h> 54#include <IOKit/kext/macho_util.h> 55#include <IOKit/storage/CoreStorage/CoreStorageUserLib.h> 56#include <IOKit/storage/CoreStorage/CoreStorageCryptoIDs.h> 57#include <IOKit/storage/CoreStorage/CSFullDiskEncryption.h> 58 59// Kext Management pieces from IOKitUser 60#include <IOKit/kext/OSKext.h> 61#include <IOKit/kext/OSKextPrivate.h> 62 63#include "bootcaches.h" // includes CF 64#include "bootroot_internal.h" // kBRUpdateOpts_t 65#include "fork_program.h" 66#include "kext_tools_util.h" 67#include "safecalls.h" 68 69// only used here 70#define kBRDiskArbMaxRetries (10) 71 72static void removeTrailingSlashes(char * path); 73#if DEV_KERNEL_SUPPORT 74static int getExtraKernelCachePaths(struct bootCaches *caches); 75#endif 76 77// XX These are used often together, rely on 'finish', and should be all-caps. 78// 'dst must be char[PATH_MAX]' 79// COMPILE_TIME_ASSERT fails for get_locres_info() due to char[size] ~ char* 80#define pathcpy(dst, src) do { \ 81 /* COMPILE_TIME_ASSERT(sizeof(dst) == PATH_MAX); */ \ 82 if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \ 83 } while(0) 84#define pathcat(dst, src) do { \ 85 /* COMPILE_TIME_ASSERT(sizeof(dst) == PATH_MAX); */ \ 86 if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \ 87 } while(0) 88 89 90// http://lists.freebsd.org/pipermail/freebsd-hackers/2004-February/005627.html 91#define LOGERRxlate(ctx1, ctx2, errval) do { \ 92 char *c2cpy = ctx2, ctx[256]; \ 93 if (ctx2 != NULL) { \ 94 snprintf(ctx, sizeof(ctx), "%s: %s", ctx1, c2cpy); \ 95 } else { \ 96 snprintf(ctx, sizeof(ctx), "%s", ctx1); \ 97 } \ 98 /* if necessary, modify passed-in argument so errno is returned */ \ 99 if (errval == -1) errval = errno; \ 100 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, \ 101 "%s: %s", ctx, strerror(errval)); \ 102 } while(0) 103 104/****************************************************************************** 105* destroyCaches cleans up a bootCaches structure 106******************************************************************************/ 107void 108destroyCaches(struct bootCaches *caches) 109{ 110 if (caches) { 111 if (caches->cachefd != -1) close(caches->cachefd); 112 if (caches->cacheinfo) CFRelease(caches->cacheinfo); 113 if (caches->miscpaths) free(caches->miscpaths); // free strings 114 if (caches->rpspaths) free(caches->rpspaths); 115 if (caches->exts) free(caches->exts); 116 if (caches->csfde_uuid) CFRelease(caches->csfde_uuid); 117#if DEV_KERNEL_SUPPORT 118 if (caches->extraKernelCachePaths) free(caches->extraKernelCachePaths); 119#endif 120 free(caches); 121 } 122} 123 124/****************************************************************************** 125* readCaches checks for and reads bootcaches.plist 126* it will also create directories for the caches in question if needed 127******************************************************************************/ 128// used for turning /foo/bar into :foo:bar for kTSCacheDir entries (see awk(1)) 129static void gsub(char old, char new, char *s) 130{ 131 char *p; 132 133 while((p = s++) && *p) 134 if (*p == old) 135 *p = new; 136} 137 138static int 139MAKE_CACHEDPATH(cachedPath *cpath, struct bootCaches *caches, 140 CFStringRef relstr) 141{ 142 int rval; 143 size_t fullLen; 144 char tsname[NAME_MAX]; 145 146 // check params 147 rval = EINVAL; 148 if (!(relstr)) goto finish; 149 if (CFGetTypeID(relstr) != CFStringGetTypeID()) goto finish; 150 151 // extract rpath 152 rval = EOVERFLOW; 153 if (!CFStringGetFileSystemRepresentation(relstr, cpath->rpath, 154 sizeof(cpath->rpath))) { 155 goto finish; 156 } 157 158 // tspath: rpath with '/' -> ':' 159 if (strlcpy(tsname, cpath->rpath, sizeof(tsname)) >= sizeof(tsname)) 160 goto finish; 161 gsub('/', ':', tsname); 162 fullLen = snprintf(cpath->tspath, sizeof(cpath->tspath), "%s/%s/%s", 163 kTSCacheDir, caches->fsys_uuid, tsname); 164 if (fullLen >= sizeof(cpath->tspath)) 165 goto finish; 166 167 rval = 0; 168 169finish: 170 return rval; 171} 172 173// validate bootcaches.plist dict; data -> struct 174// caller properly frees the structure if we fail 175static int 176extractProps(struct bootCaches *caches, CFDictionaryRef bcDict) 177{ 178 int rval = ENODEV; 179 CFDictionaryRef dict; // don't release 180 CFIndex keyCount; // track whether we've handled all keys 181 CFIndex rpsindex = 0; // index into rps; compared to caches->nrps @ end 182 CFStringRef str; // used to point to objects owned by others 183 CFStringRef createdStr = NULL; 184 185 rval = EFTYPE; 186 keyCount = CFDictionaryGetCount(bcDict); // start with the top 187 caches->exts = NULL; 188 caches->nexts = 0; 189#if DEV_KERNEL_SUPPORT 190 caches->kernelsCount = 0; 191#endif 192 193 // process keys for paths read "before the booter" 194 dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPreBootKey); 195 if (dict) { 196 CFArrayRef apaths; 197 CFIndex miscindex = 0; 198 199 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish; 200 // only "Additional Paths" can contain > 1 path 201 caches->nmisc = (int)CFDictionaryGetCount(dict); // init w/1 path/key 202 keyCount += CFDictionaryGetCount(dict); 203 204 // look at variable-sized member first -> right size for miscpaths 205 apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey); 206 if (apaths) { 207 CFIndex acount; 208 209 if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish; 210 acount = CFArrayGetCount(apaths); 211 // total "misc" paths = # of keyed paths + # additional paths 212 caches->nmisc += acount - 1; // kBCAdditionalPathsKey not a path 213 214 if ((unsigned int)caches->nmisc > INT_MAX/sizeof(*caches->miscpaths)) goto finish; 215 caches->miscpaths = (cachedPath*)calloc(caches->nmisc, 216 sizeof(*caches->miscpaths)); 217 if (!caches->miscpaths) goto finish; 218 219 for (/*miscindex = 0 (above)*/; miscindex < acount; miscindex++) { 220 str = CFArrayGetValueAtIndex(apaths, miscindex); 221 // MAKE_CACHEDPATH checks str != NULL && str's type 222 MAKE_CACHEDPATH(&caches->miscpaths[miscindex], caches, str); 223 } 224 keyCount--; // AdditionalPaths sub-key 225 } else { 226 // allocate enough for the top-level keys (nothing variable-sized) 227 if ((unsigned int)caches->nmisc > INT_MAX/sizeof(*caches->miscpaths)) goto finish; 228 caches->miscpaths = calloc(caches->nmisc, sizeof(cachedPath)); 229 if (!caches->miscpaths) goto finish; 230 } 231 232 str = (CFStringRef)CFDictionaryGetValue(dict, kBCLabelKey); 233 if (str) { 234 MAKE_CACHEDPATH(&caches->miscpaths[miscindex], caches, str); 235 caches->label = &caches->miscpaths[miscindex]; 236 237 miscindex++; // get ready for the next guy 238#pragma unused(miscindex) 239 keyCount--; // DiskLabel is dealt with 240 } 241 242 // add new keys here 243 keyCount--; // preboot dict 244 } 245 246 247 // process booter keys 248 dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCBootersKey); 249 if (dict) { 250 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish; 251 keyCount += CFDictionaryGetCount(dict); 252 253 str = (CFStringRef)CFDictionaryGetValue(dict, kBCEFIBooterKey); 254 if (str) { 255 MAKE_CACHEDPATH(&caches->efibooter, caches, str); 256 257 keyCount--; // EFIBooter is dealt with 258 } 259 260 str = (CFStringRef)CFDictionaryGetValue(dict, kBCOFBooterKey); 261 if (str) { 262 MAKE_CACHEDPATH(&caches->ofbooter, caches, str); 263 264 keyCount--; // BootX, check 265 } 266 267 // add new booters here 268 keyCount--; // booters dict 269 } 270 271 // process keys for paths read "after the booter [is loaded]" 272 // these are read by the booter proper, which determines which 273 // of the Rock, Paper, Scissors directories is most current 274 dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPostBootKey); 275 if (dict) { 276 CFDictionaryRef mkDict, erDict; 277 CFArrayRef apaths; 278 CFIndex acount; 279 Boolean isKernelcache = false; 280 int kcacheKeys = 0; 281 282 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish; 283 284 // we must deal with all sub-keys 285 keyCount += CFDictionaryGetCount(dict); 286 287 // Figure out how many files will be watched/copied via rpspaths 288 // start by assuming each key provides one path to watch/copy 289 caches->nrps = (int)CFDictionaryGetCount(dict); 290 291 // AdditionalPaths: 1 key -> extra RPS entries 292 apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey); 293 if (apaths) { 294 if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish; 295 acount = CFArrayGetCount(apaths); 296 297 // add in extra keys 298 // "additional paths" array is not itself a path -> add one less 299 caches->nrps += (acount - 1); 300 } 301 302 303 // EncryptedRoot has 5 subkeys 304 erDict=(CFDictionaryRef)CFDictionaryGetValue(dict,kBCEncryptedRootKey); 305 if (erDict) { 306 if (CFGetTypeID(erDict)!=CFDictionaryGetTypeID()) goto finish; 307 // erDict has one slot, but two required & copied sub-properties 308 caches->nrps++; 309 310 // the other three keys lead to a single localized resources cache 311 if (CFDictionaryGetValue(erDict,kBCCSFDELocalizationSrcKey)) { 312 caches->nrps++; 313 } 314 } 315 316 // finally allocate correctly-sized rpspaths 317 if ((unsigned int)caches->nrps > INT_MAX/sizeof(*caches->rpspaths)) 318 goto finish; 319 caches->rpspaths = (cachedPath*)calloc(caches->nrps, 320 sizeof(*caches->rpspaths)); 321 if (!caches->rpspaths) goto finish; 322 323 324 // Load up rpspaths 325 326 // populate rpspaths with AdditionalPaths; leave rpsindex -> avail 327 // (above: apaths type-checked, rpsindex initialized to zero) 328 if (apaths) { 329 for (; rpsindex < acount; rpsindex++) { 330 str = CFArrayGetValueAtIndex(apaths, rpsindex); 331 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 332 } 333 keyCount--; // handled AdditionalPaths 334 } 335 336 // com.apple.Boot.plist 337 str = (CFStringRef)CFDictionaryGetValue(dict, kBCBootConfigKey); 338 if (str) { 339 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 340 caches->bootconfig = &caches->rpspaths[rpsindex++]; 341 keyCount--; // handled BootConfig 342 } 343 344 // EncryptedRoot items 345 // two sub-keys required; one optional; one set of three optional 346 if (erDict) { 347 CFNumberRef boolRef; 348 keyCount += CFDictionaryGetCount(erDict); 349 350 str = CFDictionaryGetValue(erDict, kBCCSFDEPropertyCacheKey); 351 if (str) { 352 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 353 caches->erpropcache = &caches->rpspaths[rpsindex++]; 354 keyCount--; 355 } 356 357 // !RootVolumePropertyCache => enable "timestamp only" optimization 358 boolRef = CFDictionaryGetValue(erDict,kBCCSFDERootVolPropCacheKey); 359 if (boolRef) { 360 if (CFGetTypeID(boolRef) == CFBooleanGetTypeID()) { 361 caches->erpropTSOnly = CFEqual(boolRef, kCFBooleanFalse); 362 keyCount--; 363 } else { 364 goto finish; 365 } 366 } 367 368 // 8163405: non-localized resources 369 str = CFDictionaryGetValue(erDict, kBCCSFDEDefResourcesDirKey); 370 // XX check old key name for now 371 if (!str) str=CFDictionaryGetValue(erDict,CFSTR("ResourcesDir")); 372 if (str) { 373 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 374 caches->efidefrsrcs = &caches->rpspaths[rpsindex++]; 375 keyCount--; 376 } 377 378 // localized resource cache 379 str = CFDictionaryGetValue(erDict,kBCCSFDELocRsrcsCacheKey); 380 if (str) { 381 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 382 caches->efiloccache = &caches->rpspaths[rpsindex++]; 383 keyCount--; 384 385 // localization source material (required) 386 str = CFDictionaryGetValue(erDict, kBCCSFDELocalizationSrcKey); 387 if (str && CFGetTypeID(str) == CFStringGetTypeID() && 388 CFStringGetFileSystemRepresentation(str, 389 caches->locSource, sizeof(caches->locSource))) { 390 keyCount--; 391 } else { 392 goto finish; 393 } 394 395 // localization prefs file (required) 396 str = CFDictionaryGetValue(erDict, kBCCSFDELanguagesPrefKey); 397 if (str && CFGetTypeID(str) == CFStringGetTypeID() && 398 CFStringGetFileSystemRepresentation(str, caches->locPref, 399 sizeof(caches->locPref))) { 400 keyCount--; 401 } else { 402 goto finish; 403 } 404 405 // background image (optional) 406 str = CFDictionaryGetValue(erDict, kBCCSFDEBackgroundImageKey); 407 if (str && CFGetTypeID(str) == CFStringGetTypeID() && 408 CFStringGetFileSystemRepresentation(str, caches->bgImage, 409 sizeof(caches->bgImage))) { 410 keyCount--; 411 } 412 } 413 414 keyCount--; // handled EncryptedRoot 415 } 416 417 // we support any one of three kext archival methods 418 kcacheKeys = 0; 419 if (CFDictionaryContainsKey(dict, kBCMKextKey)) kcacheKeys++; 420 if (CFDictionaryContainsKey(dict, kBCMKext2Key)) kcacheKeys++; 421 if (CFDictionaryContainsKey(dict, kBCKernelcacheV1Key)) kcacheKeys++; 422 if (CFDictionaryContainsKey(dict, kBCKernelcacheV2Key)) kcacheKeys++; 423 if (CFDictionaryContainsKey(dict, kBCKernelcacheV3Key)) kcacheKeys++; 424 425 if (kcacheKeys > 1) { 426 // don't support multiple types of kernel caching ... 427 goto finish; 428 } 429 430 /* Handle the "Kernelcache" key for prelinked kernels for Lion and 431 * later, the "MKext2 key" for format-2 mkext on Snow Leopard, and the 432 * original "MKext" key for format-1 mkexts prior to SnowLeopard. 433 */ 434 do { 435 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV1Key); 436 if (!mkDict) { 437 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV2Key); 438 } 439 if (!mkDict) { 440 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV3Key); 441 } 442 443 if (mkDict) { 444 isKernelcache = true; 445 break; 446 } 447 448 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKext2Key); 449 if (mkDict) break; 450 451 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKextKey); 452 if (mkDict) break; 453 } while (0); 454 455 if (mkDict) { 456 if (CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) { 457 goto finish; 458 } 459 460 // path to the cache itself 461 // currently /System/Library/Caches/com.apple.kext.caches/Startup/kernelcache 462 str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCPathKey); 463 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); // M 464 caches->kext_boot_cache_file = &caches->rpspaths[rpsindex++]; 465#pragma unused(rpsindex) 466 467 // Starting with Kernelcache v1.3 kBCExtensionsDirKey is a key for 468 // an array of paths to extensions directory. Pre v1.3 it is just 469 // a string equal to "/System/Library/Extensions" 470 size_t bufsize = 0; 471 apaths = (CFArrayRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey); 472 if (apaths && CFArrayGetTypeID() == CFGetTypeID(apaths)) { 473 int i; 474 char *bufptr; 475 char tempbuf[PATH_MAX]; 476 477 caches->nexts = (int) CFArrayGetCount(apaths); 478 if (caches->nexts == 0) goto finish; 479 480 caches->exts = malloc(caches->nexts * PATH_MAX); 481 if (caches->exts == NULL) { 482 OSKextLogMemError(); 483 goto finish; 484 } 485 bufptr = caches->exts; 486 487 for (i = 0; i < caches->nexts; i++) { 488 str = CFArrayGetValueAtIndex(apaths, i); 489 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) { 490 goto finish; 491 } 492 if (!CFStringGetFileSystemRepresentation(str, tempbuf, 493 sizeof(tempbuf))) { 494 goto finish; 495 } 496 pathcpy(bufptr, tempbuf); 497 bufsize += (strlen(tempbuf) + 1); 498 bufptr += (strlen(tempbuf) + 1); 499 } 500 } 501 else { 502 // Pre v1.3 so we're dealing with just 1 path 503 caches->exts = malloc(PATH_MAX); 504 if (caches->exts == NULL) { 505 OSKextLogMemError(); 506 goto finish; 507 } 508 caches->nexts = 1; 509 str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey); 510 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) { 511 goto finish; 512 } 513 if (!CFStringGetFileSystemRepresentation(str, caches->exts, 514 PATH_MAX)) { 515 goto finish; 516 } 517 bufsize = (strlen(caches->exts) + 1); 518 } 519 // trim if possible 520 if (bufsize) { 521 caches->exts = reallocf(caches->exts, bufsize); 522 if (caches->exts == NULL) { 523 OSKextLogMemError(); 524 goto finish; 525 } 526 } 527 528 // kernelcaches have a kernel path key, which we set up by hand 529 if (isKernelcache) { 530 // <= 10.9 - /Volumes/foo/mach_kernel 531 // > 10.9 - /Volumes/foo/System/Library/Kernels/kernel 532 str = (CFStringRef)CFDictionaryGetValue(mkDict, 533 kBCKernelPathKey); 534 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) { 535 goto finish; 536 } 537 538 if (!CFStringGetFileSystemRepresentation(str, caches->kernelpath, 539 sizeof(caches->kernelpath))) { 540 goto finish; 541 } 542#if DEV_KERNEL_SUPPORT 543 getExtraKernelCachePaths(caches); 544#endif 545 546 } 547 548 // Archs are fetched from the cacheinfo dictionary when needed 549 keyCount--; // mkext, mkext2, or kernelcache key handled 550 } 551 552 keyCount--; // postBootPaths handled 553 } 554 555 if (keyCount == 0 && (unsigned)rpsindex == caches->nrps) { 556 rval = 0; 557 caches->cacheinfo = CFRetain(bcDict); // for archs, etc 558 } 559 560finish: 561 if (createdStr) CFRelease(createdStr); 562 if (rval != 0 && caches->exts != NULL) { 563 free(caches->exts); 564 caches->exts = NULL; 565 caches->nexts = 0; 566 } 567 568 return rval; 569} 570 571#if DEV_KERNEL_SUPPORT 572 573/* getExtraKernelCachePaths: 574 * creates and populates extraKernelCachePaths. Also sets kernelsCount. 575 * Looks for any kernel files in the format of "kernel.SUFFIX" in 576 * /System/Library/Kernels then uses SUFFIX to create the corresponding 577 * kernelcache.SUFFIX path. 578 * 579 * NOTE, we already have the kernelcache path for the "kernel" file as part of 580 * rpspaths so we do not add it to extraKernelCachePaths (thus "extra"). And 581 * that is why nekcp is one less than bootCaches.kernelsCount. 582 */ 583static int 584getExtraKernelCachePaths(struct bootCaches *caches) 585{ 586 int result = -1; 587 int maxCacheCount = 0; 588 int cacheIndex = 0; 589 CFURLRef kernURL = NULL; // must release 590 CFURLRef kernParentURL = NULL; // must release 591 CFURLEnumeratorRef myEnumerator = NULL; // must release 592 CFURLRef enumURL = NULL; // do not release 593 CFStringRef tmpCFString = NULL; // must release 594 CFArrayRef resultArray = NULL; // must release 595 char * tmpKernelPath = NULL; // must free 596 char * tmpCachePath = NULL; // must free 597 char * suffixPtr = NULL; // must free 598 599 caches->kernelsCount = 0; 600 caches->nekcp = 0; 601 602 tmpKernelPath = malloc(PATH_MAX); 603 tmpCachePath = malloc(PATH_MAX); 604 605 if (tmpKernelPath == NULL || tmpCachePath == NULL) goto finish; 606 607 if (strlcpy(tmpKernelPath, caches->root, PATH_MAX) >= PATH_MAX) 608 goto finish; 609 if (strlcat(tmpKernelPath, caches->kernelpath, PATH_MAX) >= PATH_MAX) 610 goto finish; 611 612 kernURL = CFURLCreateFromFileSystemRepresentation( 613 NULL, 614 (const UInt8 *)tmpKernelPath, 615 strlen(tmpKernelPath), 616 true ); 617 if (kernURL == NULL) { 618 goto finish; 619 } 620 621 kernParentURL = CFURLCreateCopyDeletingLastPathComponent(NULL, 622 kernURL ); 623 if (kernParentURL == NULL) { 624 goto finish; 625 } 626 627 // bail out if the parent is not "Kernels" 628 tmpCFString = CFURLCopyLastPathComponent(kernParentURL); 629 if (tmpCFString == NULL || 630 CFStringCompare(tmpCFString, CFSTR("Kernels"), 0) != kCFCompareEqualTo) { 631 goto finish; 632 } 633 634 myEnumerator = CFURLEnumeratorCreateForDirectoryURL( 635 NULL, 636 kernParentURL, 637 kCFURLEnumeratorDefaultBehavior, 638 NULL ); 639 if (myEnumerator == NULL) { 640 goto finish; 641 } 642 643 while (CFURLEnumeratorGetNextURL(myEnumerator, 644 &enumURL, 645 NULL) == kCFURLEnumeratorSuccess) { 646 SAFE_RELEASE_NULL(tmpCFString); 647 SAFE_FREE_NULL(suffixPtr); 648 649 // valid kernel name must be in the form of: 650 // "kernel" or 651 // "kernel.SUFFIX" 652 tmpCFString = CFURLCopyLastPathComponent(enumURL); 653 if (tmpCFString == NULL) continue; 654 655 if (kCFCompareEqualTo == CFStringCompare(tmpCFString, 656 CFSTR("kernel"), 657 kCFCompareAnchored)) { 658 // this is "kernel" so count it and move on 659 caches->kernelsCount++; 660 continue; 661 } 662 663 // only want kernel.SUFFIX from here on 664 if (CFStringHasPrefix(tmpCFString, 665 CFSTR("kernel.")) == false) { 666 continue; 667 } 668 669 // skip any kernels with more than one '.' character. 670 // For example: kernel.foo.bar is not a valid kernel name 671 SAFE_RELEASE_NULL(resultArray); 672 resultArray = CFStringCreateArrayWithFindResults( 673 NULL, 674 tmpCFString, 675 CFSTR("."), 676 CFRangeMake(0, CFStringGetLength(tmpCFString)), 677 0); 678 if (resultArray && CFArrayGetCount(resultArray) > 1) { 679 continue; 680 } 681 caches->kernelsCount++; 682 683 SAFE_RELEASE(tmpCFString); 684 tmpCFString = CFURLCopyPathExtension(enumURL); 685 if (tmpCFString == NULL) continue; 686 687 // build kernelcache file for each valid kernel suffix we find. 688 suffixPtr = createUTF8CStringForCFString(tmpCFString); 689 if (suffixPtr == NULL) continue; 690 691 if (strlcpy(tmpCachePath, caches->kext_boot_cache_file->rpath, PATH_MAX) >= PATH_MAX) 692 continue; 693 if (strlcat(tmpCachePath, ".", PATH_MAX) >= PATH_MAX) 694 continue; 695 if (strlcat(tmpCachePath, suffixPtr, PATH_MAX) >= PATH_MAX) 696 continue; 697 698 SAFE_RELEASE_NULL(tmpCFString); 699 tmpCFString = CFStringCreateWithCString(kCFAllocatorDefault, 700 tmpCachePath, 701 kCFStringEncodingUTF8); 702 if (tmpCFString == NULL) continue; 703 if (cacheIndex >= maxCacheCount) { 704 cachedPath * tempCPPtr; 705 706 maxCacheCount += 2; 707 tempCPPtr = (cachedPath *) calloc(maxCacheCount, sizeof(cachedPath)); 708 if (tempCPPtr == NULL) goto finish; 709 710 if (caches->extraKernelCachePaths) { 711 // copy existing cache paths into new buffer 712 bcopy(caches->extraKernelCachePaths, 713 tempCPPtr, 714 sizeof(*caches->extraKernelCachePaths) * caches->nekcp); 715 SAFE_FREE(caches->extraKernelCachePaths); 716 } 717 caches->extraKernelCachePaths = tempCPPtr; 718 } 719 720 MAKE_CACHEDPATH(&caches->extraKernelCachePaths[cacheIndex], 721 caches, 722 tmpCFString); 723 cacheIndex++; 724 caches->nekcp++; 725 } // while loop 726 727 result = 0; 728 729finish: 730 if (result != 0) { 731 SAFE_FREE_NULL(caches->extraKernelCachePaths); 732 caches->nekcp = 0; 733 } 734 SAFE_RELEASE(kernURL); 735 SAFE_RELEASE(kernParentURL); 736 SAFE_RELEASE(myEnumerator); 737 SAFE_RELEASE(tmpCFString); 738 SAFE_RELEASE(resultArray); 739 SAFE_FREE(tmpKernelPath); 740 SAFE_FREE(tmpCachePath); 741 SAFE_FREE(suffixPtr); 742 743#if 0 744 OSKextLogCFString(NULL, 745 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 746 CFSTR("%s: kernelsCount %d nekcp %d"), 747 __func__, 748 caches->kernelsCount, 749 caches->nekcp); 750 if (result == 0) { 751 int j; 752 for (j = 0; j < cacheIndex; j++) { 753 OSKextLogCFString(NULL, 754 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 755 CFSTR("%s: extraKernelCachePaths at %d \"%s\""), 756 __func__, 757 j, 758 caches->extraKernelCachePaths[j].rpath); 759 } 760 } 761#endif 762 763 return result; 764} 765 766#endif 767 768// helper to create cache dirs as needed 769static int 770ensureCacheDirs(struct bootCaches *caches) 771{ 772 int errnum, result = ELAST + 1; 773 struct statfs sfs; 774 char *errname; 775 struct stat sb; 776 char cachedir[PATH_MAX], uuiddir[PATH_MAX]; // bootstamps, csfde 777 Boolean checkOnly = false; 778 779 // don't create new cache directories if owners are disabled 780 errname = caches->root; 781 if (statfs(caches->root, &sfs) == 0) { 782 if (sfs.f_flags & MNT_IGNORE_OWNERSHIP) { 783 checkOnly = true; 784 } 785 } else { 786 result = errno; goto finish; 787 } 788 789 // bootstamps directory 790 // (always made because it's used by libbless on non-BootRoot for ESP) 791 errname = kTSCacheDir; 792 pathcpy(cachedir, caches->root); 793 pathcat(cachedir, kTSCacheDir); 794 pathcpy(uuiddir, cachedir); 795 pathcat(uuiddir, "/"); 796 pathcat(uuiddir, caches->fsys_uuid); 797 if ((errnum = stat(uuiddir, &sb))) { 798 if (errno == ENOENT) { 799 if (checkOnly) { 800 result = ENOTSUP; goto finish; 801 } 802 // attempt to clean up cache dir to eliminate stale UUID dirs 803 if (stat(cachedir, &sb) == 0) { 804 (void)sdeepunlink(caches->cachefd, cachedir); 805 } 806 // s..mkdir ensures the cache directory is on the same volume 807 if ((errnum = sdeepmkdir(caches->cachefd,uuiddir,kCacheDirMode))){ 808 result = errnum; goto finish; 809 } 810 } else { 811 result = errnum; goto finish; 812 } 813 } 814 815 // create /S/L/Caches/com.apple.corestorage as necessary 816 if (caches->erpropcache) { 817 errname = caches->erpropcache->rpath; 818 pathcpy(cachedir, caches->root); 819 pathcat(cachedir, dirname(caches->erpropcache->rpath)); 820 errname = cachedir; 821 if ((-1 == stat(cachedir, &sb))) { 822 if (errno == ENOENT) { 823 if (checkOnly) { 824 result = ENOTSUP; goto finish; 825 } 826 // s..mkdir ensures cachedir is on the same volume 827 errnum=sdeepmkdir(caches->cachefd,cachedir,kCacheDirMode); 828 if (errnum) { 829 result = errnum; goto finish; 830 } 831 } else { 832 result = errno; goto finish; 833 } 834 } 835 } 836 837 // success 838 errname = NULL; 839 result = 0; 840 841// XX need to centralize this sort of error decoding (w/9217695?) 842finish: 843 if (result) { 844 LOGERRxlate(errname, NULL, result); 845 846 // so kextcache -u doesn't claim bootcaches.plist didn't exist, etc 847 errno = 0; 848 } 849 850 return result; 851} 852 853static CFDictionaryRef 854copy_dict_from_fd(int fd, struct stat *sb) 855{ 856 CFDictionaryRef rval = NULL; 857 void *buf = NULL; 858 CFDataRef data = NULL; 859 CFDictionaryRef dict = NULL; 860 861 // read the plist 862 if (sb->st_size > UINT_MAX || sb->st_size > LONG_MAX) goto finish; 863 if (!(buf = malloc((size_t)sb->st_size))) goto finish; 864 if (read(fd, buf, (size_t)sb->st_size) != sb->st_size) 865 goto finish; 866 if (!(data = CFDataCreate(nil, buf, (long)sb->st_size))) 867 goto finish; 868 869 // Sec: see 4623105 & related for an assessment of our XML parsers 870 dict = (CFDictionaryRef) 871 CFPropertyListCreateWithData(nil, 872 data, 873 kCFPropertyListImmutable, 874 NULL, 875 NULL); 876 if (!dict || CFGetTypeID(dict)!=CFDictionaryGetTypeID()) { 877 goto finish; 878 } 879 880 rval = CFRetain(dict); 881 882finish: 883 if (dict) CFRelease(dict); // CFRetain()'d on success 884 if (data) CFRelease(data); 885 if (buf) free(buf); 886 887 return rval; 888} 889 890/* 891 * readBootCaches() reads a volumes bootcaches.plist file and returns 892 * the contents in a new struct bootCaches. Because it returns a pointer, 893 * it stores a more precise error code in errno. 894 */ 895struct bootCaches* 896readBootCaches(char *volRoot, BRUpdateOpts_t opts) 897{ 898 struct bootCaches *rval = NULL, *caches = NULL; 899 int errnum = ELAST + 1; 900 char *errmsg; 901 struct statfs rootsfs; 902 struct stat sb; 903 char bcpath[PATH_MAX]; 904 CFDictionaryRef bcDict = NULL; 905 uuid_t vol_uuid; 906 907 errmsg = "allocation failure"; 908 caches = calloc(1, sizeof(*caches)); 909 if (!caches) goto finish; 910 caches->cachefd = -1; // set cardinal (fd 0 valid) 911 pathcpy(caches->root, volRoot); 912 913 errmsg = "error opening " kBootCachesPath; 914 pathcpy(bcpath, caches->root); 915 pathcat(bcpath, kBootCachesPath); 916 // Sec: cachefd lets us validate data, operations 917 caches->cachefd = (errnum = open(bcpath, O_RDONLY|O_EVTONLY)); 918 if (errnum == -1) { 919 if (errno == ENOENT) { 920 // let kextcache -u log this special case 921 errmsg = NULL; 922 } 923 goto finish; 924 } 925 926 // check the owner and mode (fstat() to insure it's the same file) 927 // w/Leopard, root can see all the way to the disk; 99 -> truly unknown 928 // note: 'sudo cp mach_kernel /Volumes/disrespected/' should -> error 929 if (fstatfs(caches->cachefd, &rootsfs)) { 930 goto finish; 931 } 932 if (fstat(caches->cachefd, &sb)) { 933 goto finish; 934 } 935 // stash the timestamps for later reference (detect bc.plist changes) 936 caches->bcTime = sb.st_mtimespec; // stash so we can detect changes 937 if (rootsfs.f_flags & MNT_QUARANTINE) { 938 errmsg = kBootCachesPath " quarantined"; 939 goto finish; 940 } 941 if (sb.st_uid != 0) { 942 errmsg = kBootCachesPath " not owned by root; no rebuilds"; 943 goto finish; 944 } 945 if (sb.st_mode & S_IWGRP || sb.st_mode & S_IWOTH) { 946 errmsg = kBootCachesPath " writable by non-root"; 947 goto finish; 948 } 949 950 // get UUIDs & other info 951 errmsg = "error obtaining storage information"; 952 if ((errnum = copyVolumeInfo(volRoot, &vol_uuid, &caches->csfde_uuid, 953 caches->bsdname, caches->defLabel))){ 954 errno = errnum; goto finish; 955 } 956 if ((opts & kBRAnyBootStamps) == 0) { 957 uuid_unparse_upper(vol_uuid, caches->fsys_uuid); 958 } 959 960 961 // plist -> dictionary 962 errmsg = "error reading " kBootCachesPath; 963 bcDict = copy_dict_from_fd(caches->cachefd, &sb); 964 if (!bcDict) goto finish; 965 966 967 // error returned via errno now all that matters 968 errmsg = NULL; 969 970 // extractProps returns EFTYPE if the contents were whack 971 // this function returns NULL on failure -> sends err# via errno :P 972 if ((errnum = extractProps(caches, bcDict))) { 973 errno = errnum; goto finish; 974 } 975 976 977 // success! 978 rval = caches; 979 980finish: 981 // report any error message 982 if (errmsg) { 983 if (errnum == -1) { 984 OSKextLog(/* kext */ NULL, 985 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 986 "%s: %s: %s.", caches->root, errmsg, strerror(errno)); 987 } else { 988 OSKextLog(/* kext */ NULL, 989 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 990 "%s: %s.", caches->root, errmsg); 991 } 992 } 993 994 // clean up (unwind in reverse order of allocation) 995 if (bcDict) CFRelease(bcDict); // extractProps() retains for struct 996 997 // if things went awry, free anything associated with 'caches' 998 if (!rval) { 999 destroyCaches(caches); // closes cachefd if needed 1000 } 1001 1002 return rval; 1003} 1004 1005struct bootCaches* 1006readBootCachesForDADisk(DADiskRef dadisk) 1007{ 1008 struct bootCaches *rval = NULL; 1009 CFDictionaryRef ddesc = NULL; 1010 CFURLRef volURL; // owned by dict; don't release 1011 char volRoot[PATH_MAX]; 1012 int ntries = 0; 1013 1014 // Work around inability to know if diskarb is up (4243227) by retrying 1015 // until data is available. 5454260 tracks removal of the workaround. 1016 // kexd's vol_appeared() also filters volumes w/o mount points. 1017 do { 1018 ddesc = DADiskCopyDescription(dadisk); 1019 if (!ddesc) goto finish; 1020 volURL = CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumePathKey); 1021 if (volURL) { 1022 break; 1023 } else { 1024 sleep(1); 1025 CFRelease(ddesc); 1026 ddesc = NULL; 1027 } 1028 } while (++ntries < kBRDiskArbMaxRetries); 1029 1030 if (!volURL) { 1031 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1032 "Disk description missing mount point for %d tries", ntries); 1033 goto finish; 1034 } 1035 1036 if (ntries) { 1037 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1038 "Warning: readCaches got mount point after %d tries.", ntries); 1039 } 1040 1041 if (!CFURLGetFileSystemRepresentation(volURL, /* resolveToBase */ true, 1042 (UInt8 *)volRoot, sizeof(volRoot))){ 1043 OSKextLogStringError(NULL); 1044 goto finish; 1045 } 1046 1047 rval = readBootCaches(volRoot, kBROptsNone); 1048 1049finish: 1050 if (ddesc) CFRelease(ddesc); 1051 1052 return rval; 1053} 1054 1055/******************************************************************************* 1056* needsUpdate checks a single path and timestamp; populates path->tstamp 1057* compares/stores *ctime* of the source file vs. the *mtime* of the bootstamp. 1058* returns false on error: if we can't tell, we probably can't update 1059*******************************************************************************/ 1060Boolean 1061needsUpdate(char *root, cachedPath* cpath) 1062{ 1063 Boolean outofdate = false; 1064 Boolean rfpresent, tsvalid; 1065 struct stat rsb, tsb; 1066 char fullrp[PATH_MAX], fulltsp[PATH_MAX]; 1067 1068 // create full paths 1069 pathcpy(fullrp, root); 1070 pathcat(fullrp, cpath->rpath); 1071 pathcpy(fulltsp, root); 1072 pathcat(fulltsp, cpath->tspath); 1073 1074 // check the source file in the root volume 1075 if (stat(fullrp, &rsb) == 0) { 1076 rfpresent = true; 1077 } else if (errno == ENOENT) { 1078 rfpresent = false; 1079 } else { 1080 // non-ENOENT errars => fail with log message 1081 OSKextLog(/* kext */ NULL, 1082 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1083 "Cached file %s: %s.", fullrp, strerror(errno)); 1084 goto finish; 1085 } 1086 1087 // The timestamp file's mtime tracks the source file's ctime. 1088 // If present, store the root path's timestamps to apply later. 1089 if (rfpresent) { 1090 TIMESPEC_TO_TIMEVAL(&cpath->tstamps[0], &rsb.st_atimespec); 1091 TIMESPEC_TO_TIMEVAL(&cpath->tstamps[1], &rsb.st_ctimespec); 1092 } else { 1093 // "no [corresponding] root file" is represented by a timestamp 1094 // file ("bootstamp") with a/mtime == 0. 1095 bzero(cpath->tstamps, sizeof(cpath->tstamps)); 1096 } 1097 1098 // check on the timestamp file itself 1099 // it's invalid if it tracks a non-existant root file 1100 if (stat(fulltsp, &tsb) == 0) { 1101 if (tsb.st_mtimespec.tv_sec != 0) { 1102 tsvalid = true; 1103 } else { 1104 tsvalid = false; 1105 } 1106 } else if (errno == ENOENT) { 1107 tsvalid = false; 1108 } else { 1109 // non-ENOENT errors => fail w/log message 1110 OSKextLog(/* kext */ NULL, 1111 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1112 "timestamp cache %s: %s!", fulltsp, strerror(errno)); 1113 goto finish; 1114 } 1115 1116 1117 // Depnding on root file vs. timestamp data, figure out what, if 1118 // anything, needs to be done. 1119 if (rfpresent && tsvalid) { 1120 outofdate = (tsb.st_mtimespec.tv_sec != rsb.st_ctimespec.tv_sec || 1121 tsb.st_mtimespec.tv_nsec != rsb.st_ctimespec.tv_nsec); 1122 } else if (!rfpresent && tsvalid) { 1123 // need to propagate the fact that the file no longer exists 1124 outofdate = true; 1125 } else if (rfpresent && !tsvalid) { 1126 // need to make the timestamp valid 1127 outofdate = true; 1128 } else { 1129 // !rfpresent && !tsvalid 1130 outofdate = false; 1131 } 1132 1133finish: 1134 return outofdate; 1135} 1136 1137/******************************************************************************* 1138* needUpdates() checks all cached paths to see what looks out of date 1139* 1140* needUpdates() compares the cached timestamps from the helper partition 1141* (stored in /S/L/Caches/c.a.bootstamps) to the corresponding source files 1142* in the root volume. Any caches built first into the root volume 1143* (kernel/kext, EFI Login locs, etc) should be checked and rebuilt prior 1144* to calling this function. 1145* 1146* needsUpdate() also populates the timestamp structs for updateStamps(). 1147*******************************************************************************/ 1148 1149#define kBRTaintFile ".notBootRootDefault" 1150#define MAKETAINTPATH(taintpath, mount, subdir) do { \ 1151 unsigned fullLen; \ 1152 fullLen = snprintf(taintpath, sizeof(taintpath), "%s/%s/%s", \ 1153 mount, subdir ? subdir : "", kBRTaintFile); \ 1154 if (fullLen >= sizeof(taintpath)) goto finish; \ 1155} while(0) 1156Boolean 1157notBRDefault(const char *mount, const char *subdir) 1158{ 1159 Boolean ismarked = false; 1160 char taintf[PATH_MAX]; 1161 struct stat sb; 1162 1163 MAKETAINTPATH(taintf, mount, subdir); 1164 ismarked = stat(taintf, &sb) == 0; 1165 1166finish: 1167 return ismarked; 1168} 1169 1170#define OODMSG "not cached." 1171Boolean 1172needUpdates(struct bootCaches *caches, BRUpdateOpts_t opts, 1173 Boolean *rps, Boolean *booters, Boolean *misc, 1174 OSKextLogSpec oodLogSpec) 1175{ 1176 Boolean rpsOOD, bootersOOD, miscOOD, anyOOD; 1177 cachedPath *cp; 1178 1179 // assume nothing needs updating (can't tell -> don't update) 1180 rpsOOD = bootersOOD = miscOOD = anyOOD = false; 1181 1182 // If attempting to make default-bootable, check for non-default content 1183 if ((opts & kBRUCachesAnyRoot) == false && 1184 notBRDefault(caches->root,kTSCacheDir)){ 1185 rpsOOD = bootersOOD = miscOOD = anyOOD = true; 1186 // not done yet, need to populate the tstamps! 1187 } 1188 1189 // first check RPS paths 1190 for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) { 1191 if (needsUpdate(caches->root, cp)) { 1192 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 1193 anyOOD = rpsOOD = true; 1194 } 1195 } 1196#if DEV_KERNEL_SUPPORT 1197 if (caches->extraKernelCachePaths) { 1198 for (cp = caches->extraKernelCachePaths; 1199 cp < &caches->extraKernelCachePaths[caches->nekcp]; 1200 cp++) { 1201 if (needsUpdate(caches->root, cp)) { 1202 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 1203 anyOOD = rpsOOD = true; 1204 } 1205 } 1206 } 1207#endif 1208 1209 // then booters 1210 if ((cp = &(caches->efibooter)), cp->rpath[0]) { 1211 if (needsUpdate(caches->root, cp)) { 1212 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 1213 anyOOD = bootersOOD = true; 1214 } 1215 } 1216 if ((cp = &(caches->ofbooter)), cp->rpath[0]) { 1217 if (needsUpdate(caches->root, cp)) { 1218 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 1219 anyOOD = bootersOOD = true; 1220 } 1221 } 1222 1223 // and finally misc paths (non-booter files read by EFI) 1224 // kextcache -U -Boot -> misc = NULL 1225 for (cp=caches->miscpaths; cp<&caches->miscpaths[caches->nmisc]; cp++){ 1226 if (needsUpdate(caches->root, cp)) { 1227 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 1228 anyOOD = miscOOD = true; 1229 } 1230 } 1231 1232 if (rps) *rps = rpsOOD; 1233 if (booters) *booters = bootersOOD; 1234 if (misc) *misc = miscOOD; 1235 1236 return anyOOD; 1237} 1238 1239/******************************************************************************* 1240* updateStamps runs through all of the cached paths in a struct bootCaches 1241* and applies the timestamps captured before the update 1242* not going to bother with a re-stat() of the sources for now 1243*******************************************************************************/ 1244// could/should use schdirparent, move to safecalls.[ch] 1245static int 1246_sutimes(int fdvol, char *path, int oflags, struct timeval times[2]) 1247{ 1248 int bsderr; 1249 int fd = -1; 1250 1251 // X O_RDONLY is the only way to open directories ... and the 1252 // descriptor allows timestamp updates 1253 if (-1 == (fd = sopen(fdvol, path, oflags, kCacheFileMode))) { 1254 bsderr = fd; 1255 // XX sopen() should log on its own after we get errors correct 1256 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1257 "%s: %s", path, strerror(errno)); 1258 goto finish; 1259 } 1260 if ((bsderr = futimes(fd, times))) { 1261 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1262 "futimes(<%s>): %s", path, strerror(errno)); 1263 } 1264 1265finish: 1266 if (fd != -1) close(fd); 1267 1268 return bsderr; 1269} 1270 1271// Sec review: no need to drop privs thanks to safecalls.[ch] 1272static int 1273updateStamp(char *root, cachedPath *cpath, int fdvol, int command) 1274{ 1275 int bsderr = -1; 1276 char fulltspath[PATH_MAX]; 1277 1278 pathcpy(fulltspath, root); 1279 pathcat(fulltspath, cpath->tspath); 1280 1281 // we must unlink even for ApplyTimes b/c sopen() passes O_EXCL 1282 bsderr = sunlink(fdvol, fulltspath); 1283 if (bsderr == -1 && errno == ENOENT) { 1284 bsderr = 0; 1285 } 1286 1287 if (command == kBCStampsApplyTimes) { 1288 bsderr = _sutimes(fdvol, fulltspath, O_CREAT, cpath->tstamps); 1289 } 1290 1291finish: 1292 return bsderr; 1293} 1294 1295#define BRDBG_DISABLE_EXTSYNC_F "/var/db/.BRDisableExtraSync" 1296int 1297updateStamps(struct bootCaches *caches, int command) 1298{ 1299 int anyErr = 0; // accumulates errors 1300 struct statfs sfs; 1301 cachedPath *cp; 1302 struct stat sb; 1303 1304 // don't try to apply bootstamps to a read-only volume 1305 if (statfs(caches->root, &sfs) == 0) { 1306 if ((sfs.f_flags & MNT_RDONLY)) { 1307 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1308 "Warning: %s read-only: no bootstamp updates", 1309 caches->root); 1310 return 0; // success 1311 } 1312 } 1313 1314 // allow known commands through 1315 switch (command) { 1316 case kBCStampsApplyTimes: 1317 case kBCStampsUnlinkOnly: 1318 break; 1319 1320 default: 1321 return EINVAL; 1322 } 1323 1324 // if writing stamps, make sure cache directory exists 1325 if (command == kBCStampsApplyTimes && 1326 (anyErr = ensureCacheDirs(caches))) { 1327 return anyErr; 1328 } 1329 1330 // run through all of the cached paths apply bootstamp 1331 for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) { 1332 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1333 } 1334#if DEV_KERNEL_SUPPORT 1335 if (caches->extraKernelCachePaths) { 1336 for (cp = caches->extraKernelCachePaths; 1337 cp < &caches->extraKernelCachePaths[caches->nekcp]; 1338 cp++) { 1339 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1340 } 1341 } 1342#endif 1343 if ((cp = &(caches->efibooter)), cp->rpath[0]) { 1344 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1345 } 1346 if ((cp = &(caches->ofbooter)), cp->rpath[0]) { 1347 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1348 } 1349 for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++){ 1350 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1351 } 1352 1353 // make sure stamps updates are on disk 1354 if (stat(BRDBG_DISABLE_EXTSYNC_F, &sb) == -1) { 1355 anyErr |= fcntl(caches->cachefd, F_FULLFSYNC); 1356 } 1357 1358 // bootstamps imply default content, so remove any taint 1359 anyErr |= markNotBRDefault(caches->cachefd,caches->root,kTSCacheDir,false); 1360 1361 return anyErr; 1362} 1363 1364 1365/******************************************************************************* 1366* taintDefaultStamps() adds .notBootRootDefault to com.apple.bootstamps. 1367* It takes only a helper identifier, from which it searches for any online 1368* volume that stores Boot!=Root content in the specified helper. 1369* 1370* markNotBRDefault() is an exported helper that toggles the taint on/off 1371*******************************************************************************/ 1372int 1373markNotBRDefault(int scopefd, const char *mount, const char* subdir, 1374 Boolean marking) 1375{ 1376 int rval = ELAST + 1; 1377 char taintf[PATH_MAX]; 1378 1379 if (!mount) { 1380 rval = EINVAL; goto finish; 1381 } 1382 1383 // create cookie file (unlink so sopen() O_EXCL is clean) 1384 MAKETAINTPATH(taintf, mount, subdir); 1385 (void)sunlink(scopefd, taintf); 1386 if (marking) { 1387 int fd = sopen(scopefd, taintf, O_CREAT, kCacheFileMode); 1388 if (fd == -1) { 1389 rval = errno; 1390 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1391 "Creating %s: %s", taintf, strerror(rval)); 1392 goto finish; 1393 } 1394 close(fd); 1395 } 1396 1397 // success 1398 rval = 0; 1399 1400finish: 1401 return rval; 1402} 1403 1404// search all volumes to see whether any use this helper, if found, taint 1405int 1406taintDefaultStamps(CFStringRef targetBSD) 1407{ 1408 int rval = ELAST + 1; 1409 struct bootCaches *defRootCaches = NULL; 1410 char *errmsg = NULL; 1411 int nfsys, i; 1412 size_t bufsz; 1413 struct statfs *mounts = NULL; 1414 1415 errmsg = "Error getting mount list."; 1416 if (-1 == (nfsys = getfsstat(NULL, 0, MNT_NOWAIT))) { 1417 rval = errno; goto finish; 1418 } 1419 bufsz = nfsys * sizeof(struct statfs); 1420 if (!(mounts = malloc(bufsz))) { 1421 rval = errno; goto finish; 1422 } 1423 if (-1 == getfsstat(mounts, (int)bufsz, MNT_NOWAIT)) { 1424 rval = errno; goto finish; 1425 } 1426 1427 errmsg = "error examining helper partitions"; 1428 for (i = 0; i < nfsys; i++) { 1429 struct statfs *sfs = &mounts[i]; 1430 CFArrayRef helpers; 1431 CFStringRef helper; 1432 if (sfs->f_flags & MNT_LOCAL && strcmp(sfs->f_fstypename, "devfs")) { 1433 CFURLRef volURL; 1434 volURL = CFURLCreateFromFileSystemRepresentation(nil, 1435 (UInt8*)sfs->f_mntonname, 1436 strlen(sfs->f_mntonname), 1437 1 /* isDirectory */); 1438 if (!volURL) { 1439 rval = ENOMEM; goto finish; 1440 } 1441 if ((helpers = BRCopyActiveBootPartitions(volURL))) { 1442 CFIndex hidx = CFArrayGetCount(helpers); 1443 while (hidx--) { 1444 helper = CFArrayGetValueAtIndex(helpers, hidx); 1445 if (helper && CFEqual(helper, targetBSD)) { 1446 defRootCaches = readBootCaches(sfs->f_mntonname, 0); 1447 } 1448 } 1449 CFRelease(helpers); 1450 } 1451 CFRelease(volURL); 1452 } 1453 if (defRootCaches) break; 1454 } 1455 1456 // If we found a volume, leave the "helper contents non-standard" hint 1457 errmsg = NULL; 1458 if (defRootCaches) { 1459 rval = markNotBRDefault(defRootCaches->cachefd, defRootCaches->root, 1460 kTSCacheDir, true); // logs 1461 destroyCaches(defRootCaches); 1462 } else { 1463 // success 1464 rval = 0; 1465 } 1466 1467finish: 1468 if (errmsg) { 1469 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1470 "%s", errmsg); 1471 } 1472 if (mounts) free(mounts); 1473 1474 return rval; 1475} 1476 1477/******************************************************************************* 1478* rebuild_kext_boot_cache_file fires off kextcache on the given volume 1479* XX there is a bug here that can mask a stale mkext in the Apple_Boot (4764605) 1480*******************************************************************************/ 1481int 1482rebuild_kext_boot_cache_file( 1483 struct bootCaches *caches, 1484 Boolean wait, 1485 const char * kext_boot_cache_file, 1486 const char * kernel_file) 1487{ 1488 int rval = ELAST + 1; 1489 int pid = -1; 1490 CFIndex i, argi = 0, argc = 0, narchs = 0; 1491 CFDictionaryRef pbDict, mkDict; 1492 CFArrayRef archArray; 1493 char **kcargs = NULL, **archstrs = NULL; // no [ARCH_MAX] anywhere? 1494 char * lastslash = NULL; 1495 char rcpath[PATH_MAX] = ""; 1496 struct stat sb; 1497 char full_cache_file_path[PATH_MAX] = ""; 1498 char full_cache_file_dir_path[PATH_MAX] = ""; 1499 char * fullextsp = NULL; 1500 char fullkernelp[PATH_MAX] = ""; 1501 Boolean generateKernelcache = false; 1502 int mkextVersion = 0; 1503 1504 // bootcaches.plist might not request mkext/kernelcache rebuilds 1505 if (!caches->kext_boot_cache_file 1506 ) { 1507 goto finish; 1508 } 1509 1510 fullextsp = malloc(caches->nexts * PATH_MAX); 1511 if (!fullextsp) goto finish; 1512 *fullextsp = 0x00; 1513 1514 pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey); 1515 if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish; 1516 1517 /* Try for a Kernelcache key, and if there isn't one, look for an "MKext" key. 1518 */ 1519 do { 1520 mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key); 1521 if (!mkDict) 1522 mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV2Key); 1523 if (!mkDict) { 1524 mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV3Key); 1525 } 1526 1527 if (mkDict) { 1528 generateKernelcache = true; 1529 break; 1530 } 1531 1532 mkDict = CFDictionaryGetValue(pbDict, kBCMKext2Key); 1533 if (mkDict) { 1534 mkextVersion = 2; 1535 break; 1536 } 1537 1538 mkDict = CFDictionaryGetValue(pbDict, kBCMKextKey); 1539 if (mkDict) { 1540 mkextVersion = 1; 1541 break; 1542 } 1543 1544 } while (0); 1545 1546 if (!mkDict || CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) goto finish; 1547 1548 archArray = CFDictionaryGetValue(mkDict, kBCArchsKey); 1549 if (archArray) { 1550 narchs = CFArrayGetCount(archArray); 1551 archstrs = calloc(narchs, sizeof(char*)); 1552 if (!archstrs) goto finish; 1553 } 1554 1555 // argv[0] -a x -a y -l [-n] [-r] [-K <kernel>] -c <kcache> -volume-root <vol> <exts> NULL 1556 argc = 1 + (narchs*2) + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + caches->nexts + 1; 1557 kcargs = malloc(argc * sizeof(char*)); 1558 if (!kcargs) goto finish; 1559 kcargs[argi++] = "kextcache"; 1560 1561 // convert each -arch argument into a char* and add to the vector 1562 for(i = 0; i < narchs; i++) { 1563 CFStringRef archStr; 1564 size_t archSize; 1565 1566 // get arch 1567 archStr = CFArrayGetValueAtIndex(archArray, i); 1568 if (!archStr || CFGetTypeID(archStr)!=CFStringGetTypeID()) goto finish; 1569 // XX an arch is not a pathname; EncodingASCII might be more appropriate 1570 archSize = CFStringGetMaximumSizeOfFileSystemRepresentation(archStr); 1571 if (!archSize) goto finish; 1572 // X marks the spot: over 800 lines written before I realized that 1573 // there were some serious security implications 1574 archstrs[i] = malloc(archSize); 1575 if (!archstrs[i]) goto finish; 1576 if (!CFStringGetFileSystemRepresentation(archStr,archstrs[i],archSize)) 1577 goto finish; 1578 1579 kcargs[argi++] = "-arch"; 1580 kcargs[argi++] = archstrs[i]; 1581 } 1582 1583 // BootRoot always includes local kexts 1584 kcargs[argi++] = "-local-root"; 1585 1586 // 6413843 check if it's installation media (-> add -n) 1587 pathcpy(rcpath, caches->root); 1588 removeTrailingSlashes(rcpath); // X caches->root trailing '/'? 1589 pathcat(rcpath, "/etc/rc.cdrom"); 1590 if (stat(rcpath, &sb) == 0) { 1591 kcargs[argi++] = "-network-root"; 1592 } 1593 1594 // determine proper argument to precede kext_boot_cache_file 1595 if (generateKernelcache) { 1596 // for '/' only, include all kexts loaded since boot (9130863) 1597 // TO DO: can we optimize for the install->first boot case? 1598 if (0 == strcmp(caches->root, "/")) { 1599 kcargs[argi++] = "-all-loaded"; 1600 } 1601 pathcpy(fullkernelp, caches->root); 1602 removeTrailingSlashes(fullkernelp); 1603 pathcat(fullkernelp, kernel_file); 1604 kcargs[argi++] = "-kernel"; 1605 kcargs[argi++] = fullkernelp; 1606 // prelinked kernel path below 1607 kcargs[argi++] = "-prelinked-kernel"; 1608 } else if (mkextVersion == 2) { 1609 kcargs[argi++] = "-mkext2"; 1610 } else if (mkextVersion == 1) { 1611 kcargs[argi++] = "-mkext1"; 1612 } else { 1613 // internal error! 1614 goto finish; 1615 } 1616 1617 pathcpy(full_cache_file_path, caches->root); 1618 removeTrailingSlashes(full_cache_file_path); 1619 pathcat(full_cache_file_path, kext_boot_cache_file); 1620 kcargs[argi++] = full_cache_file_path; 1621 1622 kcargs[argi++] = "-volume-root"; 1623 kcargs[argi++] = caches->root; 1624 1625 // we now support multiple extensions directories 1626 char *extsDirPtr = caches->exts; 1627 char *tempExtsDirPtr = fullextsp; 1628 1629 for (i = 0; i < caches->nexts; i++) { 1630 pathcpy(tempExtsDirPtr, caches->root); 1631 removeTrailingSlashes(tempExtsDirPtr); 1632 pathcat(tempExtsDirPtr, extsDirPtr); 1633 1634 kcargs[argi++] = tempExtsDirPtr; 1635 1636 extsDirPtr += (strlen(extsDirPtr) + 1); 1637 tempExtsDirPtr += (strlen(tempExtsDirPtr) + 1); 1638 } 1639 kcargs[argi] = NULL; 1640 1641 pathcpy(full_cache_file_dir_path, full_cache_file_path); 1642 lastslash = rindex(full_cache_file_dir_path, '/'); 1643 if (lastslash) { 1644 *lastslash = '\0'; 1645 1646 /* Make sure we have a destination directory to write the new mkext 1647 * file into (people occasionally delete the caches folder). 1648 */ 1649 if ((rval = sdeepmkdir(caches->cachefd, full_cache_file_dir_path, 1650 kCacheDirMode))) { 1651 OSKextLog(/* kext */ NULL, 1652 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1653 "failed to create cache folder %s.", full_cache_file_dir_path); 1654 // can't make dest directory, kextcache will fail, so don't bother 1655 goto finish; 1656 } 1657 1658 } 1659 rval = 0; 1660 1661 /* wait:false means the return value is <0 for fork/exec failures and 1662 * the pid of the forked process if >0. 1663 * 1664 * wait:true means the return value is <0 for fork/exec failures and 1665 * the exit status of the forked process (>=0) otherwise. 1666 */ 1667 pid = fork_program("/usr/sbin/kextcache", kcargs, wait); // logs errors 1668 1669finish: 1670 if (rval) { 1671 OSKextLog(/* kext */ NULL, 1672 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1673 "Data error before mkext rebuild."); 1674 } 1675 if (wait || pid < 0) { 1676 rval = pid; 1677 } 1678 1679 if (archstrs) { 1680 for (i = 0; i < narchs; i++) { 1681 if (archstrs[i]) free(archstrs[i]); 1682 } 1683 free(archstrs); 1684 } 1685 if (fullextsp) free(fullextsp); 1686 if (kcargs) free(kcargs); 1687 1688 return rval; 1689} 1690 1691/******************************************************************************* 1692* Check our various plist caches, for the current kernel arch only, to see if 1693* they need to be rebuilt: 1694* 1695* id -> url index (per directory) 1696* logindwindow prop/value cache for OSBundleHelper (global) 1697* 1698* This should only be called for the root volume! 1699*******************************************************************************/ 1700Boolean 1701plistCachesNeedRebuild(const NXArchInfo * kernelArchInfo) 1702{ 1703 Boolean result = true; 1704 CFArrayRef systemExtensionsFolderURLs = NULL; // need not release 1705 CFStringRef cacheBasename = NULL; // must release 1706 CFIndex count, i; 1707 1708 systemExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs(); 1709 if (!systemExtensionsFolderURLs || 1710 !CFArrayGetCount(systemExtensionsFolderURLs)) { 1711 1712 result = false; 1713 goto finish; 1714 } 1715 1716 count = CFArrayGetCount(systemExtensionsFolderURLs); 1717 for (i = 0; i < count; i++) { 1718 CFURLRef directoryURL = CFArrayGetValueAtIndex( 1719 systemExtensionsFolderURLs, i); 1720 1721 /* Check the KextIdentifiers index. 1722 */ 1723 if (!_OSKextReadCache(directoryURL, CFSTR(_kOSKextIdentifierCacheBasename), 1724 /* arch */ NULL, _kOSKextCacheFormatCFBinary, /* parseXML? */ false, 1725 /* valuesOut*/ NULL)) { 1726 1727 goto finish; 1728 } 1729 } 1730 1731 /* Check the KextPropertyValues_OSBundleHelper cache for the current kernel arch. 1732 */ 1733 cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault, 1734 /* formatOptions */ NULL, CFSTR("%s%s"), 1735 _kKextPropertyValuesCacheBasename, 1736 "OSBundleHelper"); 1737 if (!cacheBasename) { 1738 OSKextLogMemError(); 1739 result = false; // cause we don't be able to update 1740 goto finish; 1741 } 1742 1743 if (!_OSKextReadCache(systemExtensionsFolderURLs, cacheBasename, 1744 kernelArchInfo, _kOSKextCacheFormatCFXML, /* parseXML? */ false, 1745 /* valuesOut*/ NULL)) { 1746 1747 goto finish; 1748 } 1749 1750 result = false; 1751 1752finish: 1753 SAFE_RELEASE(cacheBasename); 1754 return result; 1755} 1756 1757Boolean 1758check_kext_boot_cache_file( 1759 struct bootCaches * caches, 1760 const char * cache_path, 1761 const char * kernel_path) 1762{ 1763 Boolean needsrebuild = false; 1764 char fullPath[PATH_MAX] = ""; 1765 struct stat statbuffer; 1766 time_t validModtime = 0; 1767 time_t kernelcacheModtime = 0; 1768 1769 /* Do we have a cache file (mkext or kernelcache)? 1770 * Note: cache_path is a pointer field, not a static array. 1771 */ 1772 if (cache_path == NULL) 1773 goto finish; 1774 1775 /* If so, check the mod time of the cache file vs. the extensions folder. 1776 */ 1777 // we support multiple extensions directories, use latest mod time 1778 char *bufptr; 1779 int i; 1780 bufptr = caches->exts; 1781 1782 for (i = 0; i < caches->nexts; i++) { 1783 pathcpy(fullPath, caches->root); 1784 removeTrailingSlashes(fullPath); 1785 pathcat(fullPath, bufptr); 1786 1787 if (stat(fullPath, &statbuffer) == 0) { 1788#if 0 1789 OSKextLogCFString(NULL, 1790 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 1791 CFSTR("%s - %ld <- mod time of %s "), 1792 __func__, statbuffer.st_mtime, fullPath); 1793#endif 1794 if (statbuffer.st_mtime + 1 > validModtime) { 1795 validModtime = statbuffer.st_mtime + 1; 1796 } 1797 } 1798 else { 1799 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1800 "Warning: %s: %s", fullPath, strerror(errno)); 1801 } 1802 bufptr += (strlen(bufptr) + 1); 1803 fullPath[0] = 0x00; 1804 } 1805 1806 /* Check the mod time of the appropriate kernel too, if applicable. 1807 * A kernel path in bootcaches.plist means we should have a kernelcache. 1808 * Note: kernel_path is a static array, not a pointer field. 1809 */ 1810 if (kernel_path[0]) { 1811 pathcpy(fullPath, caches->root); 1812 removeTrailingSlashes(fullPath); 1813 pathcat(fullPath, kernel_path); 1814 1815 if (stat(fullPath, &statbuffer) == -1) { 1816 OSKextLog(/* kext */ NULL, 1817 kOSKextLogBasicLevel | kOSKextLogFileAccessFlag, 1818 "Note: %s: %s", fullPath, strerror(errno)); 1819 // assert(needsrebuild == false); // we can't build w/o kernel 1820 goto finish; 1821 } 1822#if 0 1823 OSKextLogCFString(NULL, 1824 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 1825 CFSTR("%s - %ld <- mod time of %s "), 1826 __func__, statbuffer.st_mtime, fullPath); 1827#endif 1828 1829 /* The cache file should be 1 second newer than the newer of the 1830 * Extensions folder(s) or the kernel. 1831 */ 1832 if (statbuffer.st_mtime > validModtime) { 1833 validModtime = statbuffer.st_mtime + 1; 1834 } 1835 } 1836 1837 // The cache file itself 1838 needsrebuild = true; // since this stat() will fail if cache file is gone 1839 pathcpy(fullPath, caches->root); 1840 removeTrailingSlashes(fullPath); 1841 pathcat(fullPath, cache_path); 1842 if (stat(fullPath, &statbuffer) == -1) { 1843 goto finish; 1844 } 1845#if 0 1846 OSKextLogCFString(NULL, 1847 kOSKextLogGeneralFlag | kOSKextLogErrorLevel, 1848 CFSTR("%s - %ld <- mod time of %s "), 1849 __func__, statbuffer.st_mtime, fullPath); 1850#endif 1851 1852 kernelcacheModtime = statbuffer.st_mtime; 1853 needsrebuild = (kernelcacheModtime != validModtime); 1854 1855finish: 1856 return needsrebuild; 1857} 1858 1859/******************************************************************************* 1860* createDiskForMount creates a DADisk object given a mount point 1861* session is optional; one is created and released if the caller can't supply 1862*******************************************************************************/ 1863DADiskRef 1864createDiskForMount(DASessionRef session, const char *mount) 1865{ 1866 DADiskRef rval = NULL; 1867 DASessionRef dasession = NULL; 1868 CFURLRef volURL = NULL; 1869 1870 if (session) { 1871 dasession = session; 1872 } else { 1873 dasession = DASessionCreate(nil); 1874 if (!dasession) goto finish; 1875 } 1876 1877 volURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*)mount, 1878 strlen(mount), 1 /*isDirectory*/); 1879 if (!volURL) goto finish; 1880 1881 rval = DADiskCreateFromVolumePath(nil, dasession, volURL); 1882 1883finish: 1884 if (volURL) CFRelease(volURL); 1885 if (!session && dasession) 1886 CFRelease(dasession); 1887 1888 return rval; 1889} 1890 1891 1892/***************************************************************************** 1893* CoreStorage FDE check & update routines 1894* kextd calls check_csfde; kextcache calls check_, rebuild_csfde_cache() 1895*****************************************************************************/ 1896// on success, caller is responsible for releasing econtext 1897int 1898copyCSFDEInfo(CFStringRef uuidStr, CFDictionaryRef *econtext, 1899 time_t *timeStamp) 1900{ 1901 int rval = ELAST+1; 1902 CFDictionaryRef lvfprops = NULL; 1903 CFDictionaryRef ectx; 1904 CFNumberRef psRef; 1905 CFArrayRef eusers; // owned by lvfprops 1906 Boolean encrypted; 1907 1908 if (!uuidStr) { 1909 rval = EINVAL; goto finish; 1910 } 1911 1912 // 04/25/11 - gab: <rdar://problem/9168337> 1913 // can't operate without libCoreStorage func 1914 if (CoreStorageCopyFamilyProperties == NULL) { 1915 rval = ESHLIBVERS; goto finish; 1916 } 1917 1918 lvfprops = CoreStorageCopyFamilyProperties(uuidStr); 1919 if (!lvfprops) { 1920 rval = EFTYPE; goto finish; 1921 } 1922 1923 ectx = (CFMutableDictionaryRef)CFDictionaryGetValue(lvfprops, 1924 CFSTR(kCoreStorageFamilyEncryptionContextKey)); 1925 if (!ectx || CFGetTypeID(ectx) != CFDictionaryGetTypeID()) { 1926 rval = EFTYPE; goto finish; 1927 } 1928 1929 // does it have encrypted users? 1930 eusers = (CFArrayRef)CFDictionaryGetValue(ectx, CFSTR(kCSFDECryptoUsersID)); 1931 encrypted = (eusers && CFArrayGetCount(eusers)); 1932 1933 if (encrypted) { 1934 if (econtext) { 1935 *econtext = CFRetain(ectx); 1936 } 1937 if (timeStamp) { 1938 psRef = CFDictionaryGetValue(ectx, CFSTR(kCSFDELastUpdateTime)); 1939 if (psRef) { 1940 if (CFGetTypeID(psRef) != CFNumberGetTypeID() || 1941 !CFNumberGetValue(psRef,kCFNumberSInt64Type,timeStamp)){ 1942 rval = EFTYPE; goto finish; 1943 } 1944 } else { // no timestamp (odd, but maybe okay) 1945 *timeStamp = 0LL; 1946 } 1947 } 1948 } else { // not encrypted 1949 if (econtext) *econtext = NULL; 1950 if (timeStamp) *timeStamp = 0LL; 1951 } 1952 1953 rval = 0; 1954 1955finish: 1956 if (lvfprops) CFRelease(lvfprops); 1957 1958 if (rval) { 1959 OSKextLogCFString(NULL, kOSKextLogErrorLevel|kOSKextLogFileAccessFlag, 1960 CFSTR("could not copy LVF props for %@: %s"), 1961 uuidStr, strerror(rval)); 1962 } 1963 1964 return rval; 1965} 1966 1967Boolean 1968check_csfde(struct bootCaches *caches) 1969{ 1970 Boolean needsupdate = false; 1971 time_t propStamp, erStamp; 1972 char erpath[PATH_MAX]; 1973 struct stat ersb; 1974 1975 if (!caches->csfde_uuid || !caches->erpropcache) 1976 goto finish; 1977 1978 if (copyCSFDEInfo(caches->csfde_uuid, NULL, &propStamp)) 1979 goto finish; 1980 1981 // get property cache file's timestamp 1982 pathcpy(erpath, caches->root); 1983 pathcat(erpath, caches->erpropcache->rpath); 1984 if (stat(erpath, &ersb) == 0) { 1985 erStamp = ersb.st_mtimespec.tv_sec; 1986 } else { 1987 if (errno == ENOENT) { 1988 erStamp = 0LL; 1989 } else { 1990 goto finish; 1991 } 1992 } 1993 1994 // generally the timestamp advances, but != means out of date 1995 needsupdate = erStamp != propStamp; 1996 1997finish: 1998 return needsupdate; 1999} 2000 2001/* 2002 * _writeCSFDENoFD() 2003 * If possible, use CSFDEInitPropertyCache() to write 2004 * EncryptedRoot.plist.wipekey to the requested path. 2005 * 2006 * CSFDEInitPropertyCache() writes to <path>/S/L/Caches/com.apple.corestorage 2007 * so the basic algorithm is 2008 * 0) provided dstpath = /path/to/S/L/Caches/com.apple.corestorage 2009 * 1) find substring S/L/Caches/c.a.corestorage/EncryptedRoot.plist.wipekey 2010 * 2) create terminated parentpath = path/to/\0ystem/L/Caches... 2011 * 3) create /path/to/.../SystemVersion.plist if it doesn't exist 2012 * 4) call CSFDEInitPropertyCache(/path/to)! 2013 */ 2014// CSFDEInitPropertyCache() uses /S/L/E in 10.7.2+, but _writeCSFDENoFD() 2015// is only for 10.7.[01] where InitPropertyCache() uses SystemVersion.plist. 2016#define kOrigInitCookieDir "/System/Library/CoreServices" 2017#define kOrigInitCookieFile "/SystemVersion.plist" 2018#define kFDECacheFile kCSFDEPropertyCacheDir"/"kCSFDEPropertyCacheFileEncrypted 2019static int 2020_writeCSFDENoFD(int scopefd, CFDictionaryRef ectx, 2021 CFStringRef wipeKeyUUID, char *dstpath) 2022{ 2023 int bsderr, rval = ELAST + 1; // all but path*() should set 2024 int fd = -1; 2025 Boolean createdCookie = false; 2026 char parentpath[PATH_MAX], cookiepath[PATH_MAX]; 2027 char *relpath; 2028 struct stat sb; 2029 2030 // detect expected relative path to EncryptedRoot.plist.wipekey 2031 // and create terminated parentpath 2032 pathcpy(parentpath, dstpath); 2033 if (!(relpath = strstr(parentpath, kFDECacheFile))) { 2034 // path doesn't contain expected substring 2035 rval = EINVAL; LOGERRxlate(dstpath, "missing" kFDECacheFile, rval); 2036 goto finish; 2037 } 2038 relpath[0] = '\0'; // terminate parentpath[] at common parent 2039 2040 // if necessary, create sibling SystemVersion.plist 2041 pathcpy(cookiepath, parentpath); 2042 pathcat(cookiepath, kOrigInitCookieDir); 2043 if ((bsderr = sdeepmkdir(scopefd, cookiepath, kCacheDirMode))) { 2044 rval = bsderr; LOGERRxlate(cookiepath, NULL, rval); goto finish; 2045 } 2046 pathcat(cookiepath, kOrigInitCookieFile); 2047 if (0 != stat(cookiepath, &sb)) { 2048 if ((fd = sopen(scopefd, cookiepath, O_CREAT, kCacheFileMode)) < 0) { 2049 rval = errno; LOGERRxlate(cookiepath, NULL, rval); goto finish; 2050 } 2051 close(fd); 2052 createdCookie = true; 2053 } 2054 2055 // write via the 10.7.[01] function (scopefd ignored!) 2056 errno = 0; 2057 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 2058 "WARNING: no CSFDEWritePropertyCacheToFD(); " 2059 "trying CSFDEInitPropertyCache()"); 2060 if (false == CSFDEInitPropertyCache(ectx, parentpath, wipeKeyUUID)) { 2061 rval = ELAST + 1; // "internal error" :P 2062 LOGERRxlate("CSFDEInitPropertyCache", parentpath, rval); 2063 goto finish; 2064 } 2065 // make sure it did the deed 2066 if (-1 == stat(dstpath, &sb)) { 2067 rval = errno; LOGERRxlate(dstpath, NULL, rval); goto finish; 2068 } 2069 2070 // success! 2071 rval = 0; 2072 2073finish: 2074 if (createdCookie) { 2075 (void)sunlink(scopefd, cookiepath); // empty boot.?/S/L/CS okay 2076 } 2077 2078 return rval; 2079} 2080 2081// NOTE: weak-linking depends on -weak-l/-weak_framemwork *and* the 2082// function declaration being marked correctly in the header file! 2083int 2084writeCSFDEProps(int scopefd, CFDictionaryRef ectx, 2085 char *cspvbsd, char *dstpath) 2086{ 2087 int errnum, rval = ELAST + 1; 2088 CFStringRef wipeKeyUUID = NULL; 2089 char dstparent[PATH_MAX]; 2090 int erfd = -1; 2091 2092 // 9168337 didn't quite do it, see 10831618 2093 // check for required weak-linked symbol 2094 if (CoreStorageCopyPVWipeKeyUUID==NULL) { 2095 rval = ESHLIBVERS; 2096 LOGERRxlate("no CoreStorageCopyPVWipeKeyUUID()", NULL, rval); 2097 goto finish; 2098 } 2099 wipeKeyUUID = CoreStorageCopyPVWipeKeyUUID(cspvbsd); 2100 if (!wipeKeyUUID) { 2101 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 2102 "CoreStorageCopyPVWipeKeyUUID(%s) failed", cspvbsd); 2103 rval = ENODEV; goto finish; 2104 } 2105 2106 // prep (ENOENT ignored by szerofile()) 2107 if ((errnum = szerofile(scopefd, dstpath)) || 2108 ((errnum = sunlink(scopefd, dstpath)) && errno != ENOENT)) { 2109 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 2110 "WARNING: %s: %s", dstpath, strerror(errno)); 2111 } 2112 2113 // recursively create the parent directory 2114 if (strlcpy(dstparent,dirname(dstpath),PATH_MAX) >= PATH_MAX) { 2115 rval = EOVERFLOW; goto finish; 2116 } 2117 if ((errnum = sdeepmkdir(scopefd, dstparent, kCacheDirMode))) { 2118 rval = errnum; LOGERRxlate(dstparent, NULL, rval); goto finish; 2119 } 2120 2121 // use modern function if available 2122 if (CSFDEWritePropertyCacheToFD!=NULL) { 2123 // open and write to FD 2124 erfd = sopen(scopefd, dstpath, O_CREAT|O_RDWR, kCacheFileMode); 2125 if (-1 == erfd) { 2126 rval = errno; LOGERRxlate(dstpath, NULL, rval); goto finish; 2127 } 2128 if (!CSFDEWritePropertyCacheToFD(ectx, erfd, wipeKeyUUID)) { 2129 rval = ELAST + 1; // "internal error" :P 2130 LOGERRxlate("CSFDEWritePropertyCacheToFD", dstpath, rval); 2131 goto finish; 2132 } 2133 } else { 2134 // try to trick the old function into writing the cache 2135 if ((errnum = _writeCSFDENoFD(scopefd,ectx,wipeKeyUUID,dstpath))) { 2136 rval = errnum; goto finish; // error logged by function 2137 } 2138 } 2139 2140 // success 2141 rval = 0; 2142 2143finish: 2144 if (wipeKeyUUID) CFRelease(wipeKeyUUID); 2145 if (erfd != -1) close(erfd); 2146 2147 return rval; 2148} 2149 2150// write out a populated EncryptedRoot.plist.wipekey to the root volume 2151static int 2152_writeLegacyCSFDECache(struct bootCaches *caches) 2153{ 2154 int errnum, rval = ELAST + 1; 2155 CFArrayRef dataVolumes = NULL; 2156 CFStringRef bsdStr; // belongs to dataVolumes 2157 char bsdname[DEVMAXPATHSIZE]; 2158 CFDictionaryRef ectx = NULL; 2159 char *errmsg; 2160 char erpath[PATH_MAX]; 2161 int erfd = -1; 2162 2163 errmsg = "invalid argument"; 2164 if (!caches->csfde_uuid || !caches->erpropcache) { 2165 rval = EINVAL; goto finish; 2166 } 2167 2168 // hasBRBs() cares about Apple_Boot's; FDE, data partitions 2169 (void)hasBootRootBoots(caches, NULL, &dataVolumes, NULL); 2170 if (!dataVolumes || CFArrayGetCount(dataVolumes) == 0) { 2171 errmsg = "no data partition! (for wipe key)"; 2172 rval = ENODEV; goto finish; 2173 } 2174 2175 // legacy => encrypt with the first Apple_CoreStorage wipe key 2176 errmsg = "error getting volume wipe key"; 2177 bsdStr = CFArrayGetValueAtIndex(dataVolumes, 0); 2178 if (!bsdStr) { 2179 rval = ENODEV; goto finish; 2180 } 2181 if (!CFStringGetFileSystemRepresentation(bsdStr,bsdname,sizeof(bsdname))){ 2182 rval = EINVAL; goto finish; 2183 } 2184 2185 errmsg = "error getting encryption context data"; 2186 if ((errnum = copyCSFDEInfo(caches->csfde_uuid, &ectx, NULL))) { 2187 rval = errnum; goto finish; 2188 } 2189 2190 // build /<vol>/S/L/Caches/..corestorage/EncryptedRoot.plist.wipekey 2191 errmsg = "error building encryption context cache file path"; 2192 pathcpy(erpath, caches->root); 2193 pathcat(erpath, caches->erpropcache->rpath); 2194 errmsg = NULL; 2195 2196 // if not encrypted, just nuke :) 2197 if (!ectx) { 2198 (void)sunlink(caches->cachefd, erpath); 2199 rval = 0; goto finish; 2200 } 2201 2202 errmsg = NULL; // writeCSFDEProps() logs errors 2203 if ((errnum = writeCSFDEProps(caches->cachefd, ectx, bsdname, erpath))) { 2204 rval = errnum; goto finish; 2205 } 2206 2207 // success 2208 rval = 0; 2209 2210finish: 2211 if (erfd != -1) close (erfd); 2212 if (dataVolumes) CFRelease(dataVolumes); 2213 if (ectx) CFRelease(ectx); 2214 2215 if (rval && errmsg) { 2216 LOGERRxlate(caches->root, errmsg, rval); 2217 } 2218 2219 return rval; 2220} 2221 2222int 2223rebuild_csfde_cache(struct bootCaches *caches) 2224{ 2225 int errnum, rval = ELAST + 1; 2226 time_t timeStamp; 2227 char erpath[PATH_MAX] = "<unknown>"; 2228 struct timeval times[2] = {{ 0, 0 }, { 0, 0 }}; 2229 2230 if (!caches->csfde_uuid || !caches->erpropcache) { 2231 rval = EINVAL; goto finish; 2232 } 2233 2234 if ((errnum = ensureCacheDirs(caches))) { 2235 rval = errnum; goto finish; 2236 } 2237 2238 // OSes that only support single-PV CSFDE need content in erpropcache 2239 if (caches->erpropTSOnly == false) { 2240 return _writeLegacyCSFDECache(caches); // takes care of everything 2241 } 2242 2243 // otherwise, just grab the timestamp so update_boot.c knows to re-fetch 2244 if ((errnum = copyCSFDEInfo(caches->csfde_uuid, NULL, &timeStamp))) { 2245 rval = errnum; goto finish; 2246 } 2247 times[0].tv_sec = (__darwin_time_t)timeStamp; 2248 times[1].tv_sec = (__darwin_time_t)timeStamp; // mdworker -> atime 2249 2250 // build path and recreate proper timestamp 2251 pathcpy(erpath, caches->root); 2252 pathcat(erpath, caches->erpropcache->rpath); 2253 (void)sunlink(caches->cachefd, erpath); 2254 2255 if (timeStamp != 0LL) { 2256 if ((errnum = _sutimes(caches->cachefd, erpath, O_CREAT, times))) { 2257 rval = errnum; goto finish; 2258 } 2259 } 2260 2261 // success 2262 rval = 0; 2263 2264finish: 2265 // no logging above 2266 if (rval) LOGERRxlate(erpath, NULL, rval); 2267 2268 return rval; 2269} 2270 2271 2272/******************************************************************************* 2273* check_loccache() checks caches that depend on the system localization 2274* XX could use getFilePathModTimePlusOne() -- currently in kextcache_main.c 2275*******************************************************************************/ 2276// [PATH_MAX] is essentially a comment; char[] are char* after being passed 2277static int 2278get_locres_info(struct bootCaches *caches, char locRsrcDir[PATH_MAX], 2279 char prefPath[PATH_MAX], struct stat *prefsb, 2280 char locCacheDir[PATH_MAX], time_t *validModTime) 2281{ 2282 int rval = EOVERFLOW; // all other paths set rval 2283 time_t newestTime; 2284 struct stat sb; 2285 char bgImagePath[PATH_MAX]; 2286 2287 if (!validModTime) { 2288 rval = EINVAL; LOGERRxlate("get_locres_info", NULL, rval); goto finish; 2289 } 2290 2291 // build localization sources directory path 2292 pathcpy(locRsrcDir, caches->root); 2293 pathcat(locRsrcDir, caches->locSource); 2294 // get localization sources directory timestamp 2295 if (stat(locRsrcDir, &sb)) { 2296 rval = errno; LOGERRxlate(locRsrcDir, NULL, rval); goto finish; 2297 } 2298 newestTime = sb.st_mtime; 2299 2300 // prefs file path & timestamp (if it exists) 2301 pathcpy(prefPath, caches->root); 2302 pathcat(prefPath, caches->locPref); 2303 if (stat(prefPath, prefsb) == 0) { 2304 if (prefsb->st_mtime > newestTime) { 2305 newestTime = prefsb->st_mtime; 2306 } 2307 } else { 2308 if (errno != ENOENT) { 2309 rval = errno; LOGERRxlate(prefPath, NULL, rval); goto finish; 2310 } 2311 } 2312 2313 // background image file (optional) 2314 if (caches->bgImage[0]) { 2315 pathcpy(bgImagePath, caches->root); 2316 pathcat(bgImagePath, caches->bgImage); 2317 if (stat(bgImagePath, &sb) == 0 && 2318 sb.st_mtime > newestTime) { 2319 newestTime = sb.st_mtime; 2320 } 2321 } 2322 2323 // the cache directory must be one second newer than the 2324 // later of the prefs file and the source directory. 2325 *validModTime = newestTime + 1; 2326 2327 // build localized resources cache directory path 2328 pathcpy(locCacheDir, caches->root); 2329 pathcat(locCacheDir, caches->efiloccache->rpath); 2330 2331 rval = 0; 2332 2333finish: 2334 return rval; 2335} 2336 2337Boolean 2338check_loccache(struct bootCaches *caches) 2339{ 2340 Boolean needsupdate = false; // needsupdate defaults to "nope" 2341 struct stat prefsb, cachesb; 2342 char erpath[PATH_MAX]; 2343 char locRsrcDir[PATH_MAX], prefPath[PATH_MAX]; 2344 char locCacheDir[PATH_MAX]; 2345 time_t validModTime = 0; 2346 2347 if (!caches->efiloccache) goto finish; 2348 2349 // 9516786: loccache only needed if EFI Login plist is active 2350 pathcpy(erpath, caches->root); 2351 pathcat(erpath, caches->erpropcache->rpath); 2352 if (stat(erpath, &cachesb) == -1 && errno == ENOENT) { 2353 // not an error, there is no cache file on non-encrypted volumes 2354 goto finish; 2355 } 2356 2357 if (get_locres_info(caches, locRsrcDir, prefPath, &prefsb, 2358 locCacheDir, &validModTime)) { 2359 goto finish; // error logged by function 2360 } 2361 2362 if (stat(locCacheDir, &cachesb) == 0) { 2363 needsupdate = (cachesb.st_mtime != validModTime); 2364 } else if (errno == ENOENT) { 2365 needsupdate = true; 2366 } 2367 2368finish: 2369 return needsupdate; 2370} 2371 2372/***************************************************************************** 2373* rebuild_loccache() rebuilds the localized resources for EFI Login 2374*****************************************************************************/ 2375struct writeRsrcCtx { 2376 struct bootCaches *caches; 2377 char *locCacheDir; 2378 int *result; 2379}; 2380void 2381_writeResource(const void *value, void *ctxp) 2382{ 2383 int bsderr; 2384 CFDictionaryRef rsrc = (CFDictionaryRef)value; 2385 struct writeRsrcCtx *ctx = (struct writeRsrcCtx*)ctxp; 2386 struct bootCaches *caches = ctx->caches; 2387 2388 CFDataRef data; 2389 void *buf; 2390 ssize_t bufsz; 2391 CFStringRef nameStr; 2392 int fflags, fd = -1; 2393 char fname[PATH_MAX], fullp[PATH_MAX]; 2394 2395 2396 // extract data, filename & prepare for BSD syscalls 2397 bsderr = EFTYPE; 2398 if (!(data = CFDictionaryGetValue(rsrc, kEFILoginDataKey))) 2399 goto finish; 2400 if (!(buf = (void*)CFDataGetBytePtr(data))) 2401 goto finish; 2402 bufsz = (ssize_t)CFDataGetLength(data); 2403 if (bufsz < 0) goto finish; 2404 2405 if (!(nameStr = CFDictionaryGetValue(rsrc, kEFILoginFileNameKey))) 2406 goto finish; 2407 if(!CFStringGetFileSystemRepresentation(nameStr, fname, PATH_MAX)) 2408 goto finish; 2409 bsderr = EOVERFLOW; 2410 pathcpy(fullp, ctx->locCacheDir); 2411 pathcat(fullp, "/"); 2412 pathcat(fullp, fname); 2413 2414 // open & write! 2415 fflags = O_WRONLY | O_CREAT | O_TRUNC; // sopen() adds EXCL/NOFOL 2416 if (-1 == (fd = sopen(caches->cachefd, fullp, fflags, kCacheFileMode))) { 2417 bsderr = -1; 2418 goto finish; 2419 } 2420 if (write(fd, buf, bufsz) != bufsz) { 2421 bsderr = -1; 2422 goto finish; 2423 } 2424 2425 // success 2426 bsderr = 0; 2427 2428finish: 2429 if (bsderr) { 2430 *(ctx->result) = bsderr; 2431 } 2432 2433 if (fd != -1) close(fd); 2434 2435 return; 2436} 2437 2438// ahh, ye olde SysLang.h :] 2439// #define GLOBALPREFSFILE "/Library/Preferences/.GlobalPreferences.plist" 2440#define LANGSKEY CFSTR("AppleLanguages") // key in .GlobalPreferences 2441#define ENGLISHKEY CFSTR("en") 2442static int 2443_writeEFILoginResources(struct bootCaches *caches, 2444 char prefPath[PATH_MAX], struct stat *prefsb, 2445 char locCacheDir[PATH_MAX]) 2446{ 2447 int result; // all paths set an explicit result 2448 int gpfd = -1; 2449 CFDictionaryRef gprefs = NULL; 2450 CFMutableArrayRef locsList = NULL; // retained & released 2451 CFStringRef volStr = NULL; 2452 CFArrayRef blobList = NULL; 2453 2454 CFRange allEntries; 2455 struct writeRsrcCtx applyCtx = { caches, locCacheDir, &result }; 2456 2457 // can't operate without EFILogin.framework function 2458 // (XX as of Zin12A190, this function is not properly decorated ...) 2459 if (EFILoginCopyInterfaceGraphics == NULL) { 2460 result = ESHLIBVERS; 2461 goto finish; 2462 } 2463 2464 // attempt to get AppleLanguages out of .GlobalPreferences 2465 if ((gpfd = sopen(caches->cachefd, prefPath, O_RDONLY, 0)) >= 0 && 2466 (gprefs = copy_dict_from_fd(gpfd, prefsb)) && 2467 (locsList=(CFMutableArrayRef)CFDictionaryGetValue(gprefs,LANGSKEY)) && 2468 CFGetTypeID(locsList) == CFArrayGetTypeID()) { 2469 CFRetain(locsList); 2470 } else { 2471 // create a new array containing the default "en" (locsList !retained) 2472 CFRange range = { 0, 1 }; 2473 locsList = CFArrayCreateMutable(nil, 1, &kCFTypeArrayCallBacks); 2474 if (!locsList) { 2475 result = ENOMEM; 2476 goto finish; 2477 } 2478 CFArrayAppendValue(locsList, ENGLISHKEY); 2479 if (!CFArrayContainsValue(locsList, range, ENGLISHKEY)) { 2480 result = ENOMEM; // ECFFAILED :P 2481 goto finish; 2482 } 2483 } 2484 2485 // generate all resources 2486 volStr = CFStringCreateWithFileSystemRepresentation(nil, caches->root); 2487 if (!volStr || 2488 !(blobList = EFILoginCopyInterfaceGraphics(locsList, volStr))) { 2489 result = ENOMEM; 2490 goto finish; 2491 } 2492 2493 // write everything out 2494 result = 0; // applier only modifies on error 2495 allEntries = CFRangeMake(0, CFArrayGetCount(blobList)); 2496 CFArrayApplyFunction(blobList, allEntries, _writeResource, &applyCtx); 2497 if (result) goto finish; 2498 2499 // success! 2500 result = 0; 2501 2502finish: 2503 if (blobList) CFRelease(blobList); 2504 if (volStr) CFRelease(volStr); 2505 if (locsList) CFRelease(locsList); 2506 if (gprefs) CFRelease(gprefs); 2507 if (gpfd != -1) close(gpfd); 2508 2509 return result; 2510} 2511 2512int 2513rebuild_loccache(struct bootCaches *caches) 2514{ 2515 int errnum, result = ELAST + 1; 2516 struct stat cachesb, prefsb; 2517 char locRsrcDir[PATH_MAX], prefPath[PATH_MAX]; 2518 char locCacheDir[PATH_MAX]; 2519 time_t validModTime = 0; 2520 int fd = -1; 2521 struct timeval times[2]; 2522 2523 // prefsb.st_size = 0; // Analyzer doesn't check get_locres_info(&prefsb) 2524 bzero(&prefsb, sizeof(prefsb)); // and doesn't know bzero sets st_size = 0 2525 if ((errnum = get_locres_info(caches, locRsrcDir, prefPath, &prefsb, 2526 locCacheDir, &validModTime))) { 2527 result = errnum; goto finish; // error logged by function 2528 } 2529 2530 // empty out locCacheDir ... 2531 /* This cache is an optional part of RPS, thus it is okay to 2532 destroy on failure (leaving it empty risks "right" timestamps). */ 2533 if (sdeepunlink(caches->cachefd, locCacheDir) == -1 && errno == EROFS) { 2534 result = errno; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2535 } 2536 if ((errnum = sdeepmkdir(caches->cachefd,locCacheDir,kCacheDirMode))) { 2537 result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2538 } 2539 2540 // actually write resources! 2541 errnum = _writeEFILoginResources(caches, prefPath, &prefsb, locCacheDir); 2542 if (errnum) { 2543 (void)sdeepunlink(caches->cachefd, locCacheDir); 2544 result = errnum; 2545 LOGERRxlate("_writeEFILoginResources", NULL, result); 2546 goto finish; 2547 } 2548 2549 // get current times (keeping access, overwriting mod) 2550 if ((errnum = stat(locCacheDir, &cachesb))) { 2551 result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2552 } 2553 cachesb.st_mtime = validModTime; 2554 TIMESPEC_TO_TIMEVAL(×[0], &cachesb.st_atimespec); 2555 TIMESPEC_TO_TIMEVAL(×[1], &cachesb.st_mtimespec); 2556 if ((errnum = _sutimes(caches->cachefd, locCacheDir, O_RDONLY, times))) { 2557 result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2558 } 2559 2560 // success 2561 result = 0; 2562 2563finish: 2564 if (fd != -1) close(fd); 2565 2566 return result; 2567} 2568 2569 2570 2571/***************************************************************************** 2572* hasBRBoots lets you know if a volume has boot partitions and if it's on GPT 2573* no error reporting except residual errno 2574*****************************************************************************/ 2575Boolean 2576hasBootRootBoots(struct bootCaches *caches, CFArrayRef *auxPartsCopy, 2577 CFArrayRef *dataPartsCopy, Boolean *isAPM) 2578{ 2579 CFDictionaryRef binfo = NULL; 2580 Boolean rval = false, apm = false; 2581 CFArrayRef dparts = NULL, bparts = NULL; 2582 char stack_bsdname[DEVMAXPATHSIZE]; 2583 char * lookup_bsdname = caches->bsdname; 2584 CFArrayRef dataPartitions = NULL; // do not release; 2585 size_t fullLen; 2586 char fulldev[DEVMAXPATHSIZE]; 2587#if DEBUG_REGISTRY 2588 char parentdevname[DEVMAXPATHSIZE]; 2589 uint32_t partitionNum; 2590 BLPartitionType partitionType; 2591#endif 2592 2593 /* Get the BL info about partitions & such. 2594 */ 2595 if (BLCreateBooterInformationDictionary(NULL, lookup_bsdname, &binfo)) 2596 goto finish; 2597 bparts = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey); 2598 dparts = CFDictionaryGetValue(binfo, kBLDataPartitionsKey); 2599 if (!bparts || !dparts) goto finish; 2600 2601 /***** 2602 * Now, for a GPT check, use one of the data partitions given by the above 2603 * call to BLCreateBooterInformationDictionary(). 2604 */ 2605 dataPartitions = CFDictionaryGetValue(binfo, kBLDataPartitionsKey); 2606 if (dataPartitions && CFArrayGetCount(dataPartitions)) { 2607 CFStringRef dpBsdName = CFArrayGetValueAtIndex(dataPartitions, 0); 2608 2609 if (dpBsdName) { 2610 if (!CFStringGetFileSystemRepresentation(dpBsdName, stack_bsdname, 2611 sizeof(stack_bsdname))) 2612 goto finish; 2613 lookup_bsdname = stack_bsdname; 2614 } 2615 } 2616 2617 /* Get the BL info about the partition type (that's all we use, but 2618 * we have to pass in valid buffer pointers for all the rest). 2619 */ 2620 fullLen = snprintf(fulldev, sizeof(fulldev), "/dev/%s", lookup_bsdname); 2621 if (fullLen >= sizeof(fulldev)) { 2622 goto finish; 2623 } 2624 2625#if DEBUG_REGISTRY 2626 // doesn't work on watson w/USB disk?? 2627 if (BLGetParentDeviceAndPartitionType(NULL /* context */, 2628 fulldev, parentdevname, &partitionNum, &partitionType)) 2629 { 2630 goto finish; 2631 } 2632 if (partitionType == kBLPartitionType_APM) { 2633 apm = true; 2634 } 2635#endif 2636 2637 // 5158091 / 6413843: 10.4.x APM Apple_Boot's aren't BootRoot 2638 // Boot!=Root was introduced in 10.4.7 for *Intel only*. 2639 // BootX didn't learn about Boot!=Root until 10.5 (mkext2 era). 2640 // XX 10740646 tracks reviewing / dropping ppc support 2641 // The check is APM-only because ppc only booted APM. 2642 if (apm) { 2643 CFDictionaryRef pbDict, mk2Dict, kcDict; 2644 2645 // i.e. Leopard had BootX; SnowLeopard has mkext2 2646 pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey); 2647 if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish; 2648 2649 kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key); 2650 if (!kcDict) 2651 kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV2Key); 2652 mk2Dict = CFDictionaryGetValue(pbDict, kBCMKext2Key); 2653 2654 // if none of these indicates a more modern OS, we skip 2655 // XX should the ofbooter path check be != '\0' ? 2656 // (then we could drop the kcDict check?) 2657 if (!kcDict && !mk2Dict && caches->ofbooter.rpath[0] == '\0') 2658 goto finish; 2659 } 2660 2661 // check for helper partitions 2662 rval = (CFArrayGetCount(bparts) > 0); 2663 2664finish: 2665 // out parameters set if provided 2666 if (auxPartsCopy) { 2667 if (bparts) CFRetain(bparts); 2668 *auxPartsCopy = bparts; 2669 } 2670 if (dataPartsCopy) { 2671 if (dparts) CFRetain(dparts); 2672 *dataPartsCopy = dparts; 2673 } 2674 if (isAPM) *isAPM = apm; 2675 2676 // cleanup 2677 if (binfo) CFRelease(binfo); 2678 2679 return rval; 2680} 2681 2682CFArrayRef 2683BRCopyActiveBootPartitions(CFURLRef volRoot) 2684{ 2685 CFArrayRef bparts, rval = NULL; 2686 char path[PATH_MAX], *bsdname; 2687 struct statfs sfs; 2688 CFDictionaryRef binfo = NULL; 2689 2690 if (!volRoot) goto finish; 2691 2692 // get BSD Name of volRoot 2693 if (!CFURLGetFileSystemRepresentation( 2694 volRoot, false, (UInt8*)path, sizeof(path))) { 2695 goto finish; 2696 } 2697 if (-1 == statfs(path, &sfs)) goto finish; 2698 if (strlen(sfs.f_mntfromname) < sizeof(_PATH_DEV)) { 2699 goto finish; 2700 } 2701 bsdname = sfs.f_mntfromname + (sizeof(_PATH_DEV)-1); 2702 2703 // have libbless provide the helper partitions 2704 // (doesn't vet them as much as hasBootRootBoots()) 2705 if (BLCreateBooterInformationDictionary(NULL, bsdname, &binfo)) 2706 goto finish; 2707 bparts = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey); 2708 2709 // success -> retain sub-dictionary for caller 2710 if (bparts && CFArrayGetCount(bparts)) { 2711 rval = CFRetain(bparts); 2712 } 2713 2714finish: 2715 if (binfo) CFRelease(binfo); 2716 2717 return rval; 2718} 2719 2720/******************************************************************************* 2721*******************************************************************************/ 2722void 2723_daDone(DADiskRef disk __unused, DADissenterRef dissenter, void *ctx) 2724{ 2725 if (dissenter) 2726 CFRetain(dissenter); 2727 *(DADissenterRef*)ctx = dissenter; 2728 CFRunLoopStop(CFRunLoopGetCurrent()); // assumed okay even if not running 2729} 2730 2731/******************************************************************************* 2732* We don't want to wind up invoking kextcache using assembled paths that have 2733* repeating slashes. Note that paths in bootcaches.plist are absolute so 2734* appending them should always put a slash in as expected. 2735*******************************************************************************/ 2736static void removeTrailingSlashes(char * path) 2737{ 2738 size_t pathLength = strlen(path); 2739 size_t scanIndex = pathLength - 1; 2740 2741 if (!pathLength) return; 2742 2743 while (path[scanIndex] == '/') { 2744 path[scanIndex] = '\0'; 2745 if (scanIndex == 0) break; 2746 scanIndex--; 2747 } 2748 2749 return; 2750} 2751 2752 2753 2754/****************************************************************************** 2755 * updateMount() remounts the volume with the requested flags! 2756 *****************************************************************************/ 2757int 2758updateMount(mountpoint_t mount, uint32_t mntgoal) 2759{ 2760 int result = ELAST + 1; // 3/22/12: all paths set result 2761 DASessionRef session = NULL; 2762 CFStringRef toggleMode = CFSTR("updateMountMode"); 2763 CFURLRef volURL = NULL; 2764 DADiskRef disk = NULL; 2765 DADissenterRef dis = (void*)kCFNull; 2766 CFStringRef mountargs[] = { 2767 CFSTR("update"), 2768 ( mntgoal & MNT_NODEV ) ? CFSTR("nodev") : CFSTR("dev"), 2769 ( mntgoal & MNT_NOEXEC ) ? CFSTR("noexec") : CFSTR("exec"), 2770 ( mntgoal & MNT_NOSUID ) ? CFSTR("nosuid") : CFSTR("suid"), 2771 ( mntgoal & MNT_RDONLY ) ? CFSTR("rdonly") : CFSTR("rw"), 2772 ( mntgoal & MNT_DONTBROWSE ) ? CFSTR("nobrowse") : CFSTR("browse"), 2773 (mntgoal & MNT_IGNORE_OWNERSHIP) ? CFSTR("noowners") : CFSTR("owners"), 2774 NULL }; 2775 2776 // same 'dis' logic as mountBoot in update_boot.c 2777 if (!(session = DASessionCreate(nil))) { 2778 result = ENOMEM; goto finish; 2779 } 2780 DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), toggleMode); 2781 if (!(volURL=CFURLCreateFromFileSystemRepresentation(nil, (void*)mount, 2782 strlen(mount), true))) { 2783 result = ENOMEM; goto finish; 2784 } 2785 if (!(disk = DADiskCreateFromVolumePath(nil, session, volURL))) { 2786 result = ENOMEM; goto finish; 2787 } 2788 DADiskMountWithArguments(disk, NULL, kDADiskMountOptionDefault, _daDone, 2789 &dis, mountargs); 2790 2791 while (dis == (void*)kCFNull) { 2792 CFRunLoopRunInMode(toggleMode, 0, true); // _daDone updates 'dis' 2793 } 2794 if (dis) { 2795 result = DADissenterGetStatus(dis); // XX errno |= unix_err() 2796 if (result == 0) result = ELAST + 1; 2797 goto finish; 2798 } 2799 2800 result = 0; 2801 2802finish: 2803 if (dis && dis != (void*)kCFNull) CFRelease(dis); 2804 if (disk) CFRelease(disk); 2805 if (session) CFRelease(session); 2806 if (volURL) CFRelease(volURL); 2807 2808 if (result) { 2809 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 2810 "Warning: couldn't update %s->f_flags to %#x: error %#x", mount, 2811 mntgoal, result); 2812 } 2813 2814 return result; 2815} 2816 2817/****************************************************************************** 2818 * returns the result of fork/exec (negative on error; pid on success) 2819 * a (waited-for) helper exit status will also be returned (see fork_program.c) 2820 * - 'force' -> -f to ignore bootstamps (13784516 removed only use) 2821 *****************************************************************************/ 2822// kextcache -u helper sets up argv 2823pid_t 2824launch_rebuild_all(char * rootPath, Boolean force, Boolean wait) 2825{ 2826 pid_t rval = -1; 2827 int argc, argi = 0; 2828 char **kcargs = NULL; 2829 2830 // argv[0] '-F' '-u' root -f ? NULL 2831 argc = 1 + 1 + 1 + 1 + (force == true) + 1; 2832 kcargs = malloc(argc * sizeof(char*)); 2833 if (!kcargs) goto finish; 2834 2835 kcargs[argi++] = "/usr/sbin/kextcache"; 2836 // fork_program(wait=false) also sets IOPOL_THROTTLE while spawning 2837 kcargs[argi++] = "-F"; // lower priority within kextcache 2838 if (force) { 2839 kcargs[argi++] = "-f"; 2840 } 2841 kcargs[argi++] = "-u"; 2842 kcargs[argi++] = rootPath; 2843 // kextcache reads bc.plist so nothing more needed 2844 2845 kcargs[argi] = NULL; // terminate the list 2846 2847 /* wait:false means the return value is <0 for fork/exec failures and 2848 * the pid of the forked process if >0. 2849 */ 2850 rval = fork_program(kcargs[0], kcargs, wait); 2851 2852finish: 2853 if (kcargs) free(kcargs); 2854 2855 if (rval < 0) 2856 OSKextLog(/* kext */ NULL, 2857 kOSKextLogErrorLevel | kOSKextLogIPCFlag, 2858 "Error launching kextcache -u."); 2859 2860 return rval; 2861} 2862 2863/******************************************************************************* 2864*******************************************************************************/ 2865struct nameAndUUID { 2866 uint32_t nbytes; 2867 struct attrreference nameref; 2868 uuid_t uuid; 2869 char namedata[NAME_MAX+1]; 2870}; 2871int 2872copyVolumeInfo(const char *vol_path, uuid_t *vol_uuid, CFStringRef *cslvf_uuid, 2873 char vol_bsd[DEVMAXPATHSIZE], char vol_name[NAME_MAX]) 2874{ 2875 int bsderr, rval = ENODEV; 2876 struct nameAndUUID attrs; 2877 struct attrlist attrdesc = { ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO | 2878 ATTR_VOL_NAME | ATTR_VOL_UUID, 0, 0, 0 }; 2879 struct statfs sfs; 2880 char *bsdname; 2881 io_object_t ioObj = IO_OBJECT_NULL; 2882 CFTypeRef regEntry = NULL; 2883 2884 // get basic data 2885 // (don't worry about FSOPT_REPORT_FULLSIZE; NAME_MAX+1 is plenty :] 2886 if ((bsderr=getattrlist(vol_path, &attrdesc, &attrs, sizeof(attrs), 0)) 2887 || attrs.nbytes >= sizeof(attrs)) { 2888 rval = errno; goto finish; 2889 } 2890 if (vol_bsd || cslvf_uuid) { 2891 if ((bsderr = statfs(vol_path, &sfs))) { 2892 rval = errno; goto finish; 2893 } 2894 bsdname = sfs.f_mntfromname; 2895 if (strncmp(bsdname, _PATH_DEV, strlen(_PATH_DEV)) == 0) { 2896 bsdname += strlen(_PATH_DEV); 2897 } 2898 } 2899 2900 2901 // handle UUID if requested 2902 if (vol_uuid) { 2903 memcpy(*vol_uuid, attrs.uuid, sizeof(uuid_t)); 2904 } 2905 2906 // CoreStorage UUID if requested 2907 if (cslvf_uuid) { 2908 CFDictionaryRef matching; // IOServiceGetMatchingServices() releases 2909 matching = IOBSDNameMatching(kIOMasterPortDefault, 0, bsdname); 2910 if (!matching) { 2911 rval = ENOMEM; goto finish; 2912 } 2913 ioObj = IOServiceGetMatchingService(kIOMasterPortDefault, matching); 2914 matching = NULL; // IOServiceGetMatchingService() released 2915 if (ioObj == IO_OBJECT_NULL) { 2916 rval = ENODEV; goto finish; 2917 } 2918 regEntry = IORegistryEntryCreateCFProperty(ioObj, 2919 CFSTR(kCoreStorageLVFUUIDKey), nil, 0); 2920 if (regEntry && CFGetTypeID(regEntry) == CFStringGetTypeID()) { 2921 // retain the result (regEntry released below) 2922 *cslvf_uuid = (CFStringRef)CFRetain(regEntry); 2923 } else { 2924 *cslvf_uuid = NULL; 2925 } 2926 } 2927 2928 // BSD Name 2929 if (vol_bsd) { 2930 if (strlcpy(vol_bsd, bsdname, DEVMAXPATHSIZE) >= DEVMAXPATHSIZE) { 2931 rval = EOVERFLOW; goto finish; 2932 } 2933 } 2934 2935 // volume name 2936 if (vol_name) { 2937 char *volname = (char*)&attrs.nameref + attrs.nameref.attr_dataoffset; 2938 (void)strlcpy(vol_name, volname, NAME_MAX); 2939 } 2940 2941 rval = 0; 2942 2943finish: 2944 if (rval) { 2945 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 2946 "%s: %s", vol_path, strerror(rval)); 2947 } 2948 2949 if (regEntry) CFRelease(regEntry); 2950 if (ioObj != IO_OBJECT_NULL) IOObjectRelease(ioObj); 2951 // matching consumed by IOServiceGetMatchingService() 2952 2953 return rval; 2954} 2955 2956