2  * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The Original Code and all software distributed under the License are 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25 // machorep - DiskRep mix-in for handling Mach-O main executables 
  28 #include "StaticCode.h" 
  30 #include <security_utilities/logging.h> 
  31 #include <security_utilities/cfmunge.h> 
  36 namespace CodeSigning 
{ 
  38 using namespace UnixPlusPlus
; 
  43 // We open the main executable lazily, so nothing much happens on construction. 
  44 // If the context specifies a file offset, we directly pick that Mach-O binary (only). 
  45 // if it specifies an architecture, we try to pick that. Otherwise, we deliver the whole 
  46 // Universal object (which will usually deliver the "native" architecture later). 
  48 MachORep::MachORep(const char *path
, const Context 
*ctx
) 
  49         : SingleDiskRep(path
), mSigningData(NULL
) 
  53                         mExecutable 
= new Universal(fd(), (size_t)ctx
->offset
, ctx
->size
); 
  55                         auto_ptr
<Universal
> full(new Universal(fd())); 
  56                         mExecutable 
= new Universal(fd(), full
->archOffset(ctx
->arch
), full
->archLength(ctx
->arch
)); 
  58                         mExecutable 
= new Universal(fd()); 
  60                 mExecutable 
= new Universal(fd()); 
  63         CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path
, (void*)ctx
); 
  74 // Sniffer function for "plausible Mach-O binary" 
  76 bool MachORep::candidate(FileDesc 
&fd
) 
  78         switch (Universal::typeOf(fd
)) { 
  85                 return true;            // dynamic image; supported 
  87                 return false;           // maybe later... 
  89                 return false;           // not Mach-O (or too exotic) 
  96 // Nowadays, the main executable object is created upon construction. 
  98 Universal 
*MachORep::mainExecutableImage() 
 105 // Explicitly default to SHA256 (only) digests if the minimum deployment 
 106 // target is young enough. 
 108 void MachORep::prepareForSigning(SigningContext 
&context
) 
 110         if (context
.digestAlgorithms().empty()) { 
 111         auto_ptr
<MachO
> macho(mainExecutableImage()->architecture()); 
 113                 if (const version_min_command 
*version 
= macho
->findMinVersion()) { 
 115                         switch (macho
->flip(version
->cmd
)) { 
 116                         case LC_VERSION_MIN_MACOSX
: 
 117                                 limit 
= (10 << 16 | 11 << 8 | 4 << 0); 
 119 #if 0 /* need updated libMIS before we can do this switch */ 
 120                         case LC_VERSION_MIN_IPHONEOS
: 
 121                                 limit 
= (9 << 16 | 3 << 8); 
 123                         case LC_VERSION_MIN_WATCHOS
: 
 124                                 limit 
= (2 << 16 | 2 << 8); 
 126                         case LC_VERSION_MIN_TVOS
: 
 127                                 limit 
= (9 << 16 | 2 << 8); 
 132             case LC_VERSION_MIN_IPHONEOS
: 
 133             case LC_VERSION_MIN_WATCHOS
: 
 134             case LC_VERSION_MIN_TVOS
: 
 140                         if (macho
->flip(version
->version
) >= limit
) { 
 141                                 // young enough not to need SHA-1 legacy support 
 142                                 context
.setDigestAlgorithm(kSecCodeSignatureHashSHA256
); 
 150 // Signing base is the start of the Mach-O architecture we're using 
 152 size_t MachORep::signingBase() 
 154         return mainExecutableImage()->archOffset(); 
 157 size_t MachORep::signingLimit() 
 159         auto_ptr
<MachO
> macho(mExecutable
->architecture()); 
 160         return macho
->signingExtent(); 
 165 // We choose the binary identifier for a Mach-O binary as follows: 
 166 //      - If the Mach-O headers have a UUID command, use the UUID. 
 167 //      - Otherwise, use the SHA-1 hash of the (entire) load commands. 
 169 CFDataRef 
MachORep::identification() 
 171         std::auto_ptr
<MachO
> macho(mainExecutableImage()->architecture()); 
 172         return identificationFor(macho
.get()); 
 175 CFDataRef 
MachORep::identificationFor(MachO 
*macho
) 
 177         // if there is a LC_UUID load command, use the UUID contained therein 
 178         if (const load_command 
*cmd 
= macho
->findCommand(LC_UUID
)) { 
 179                 const uuid_command 
*uuidc 
= reinterpret_cast<const uuid_command 
*>(cmd
); 
 180                 // uuidc->cmdsize should be sizeof(uuid_command), so if it is not, 
 181                 // something is wrong. Fail out. 
 182                 if (macho
->flip(uuidc
->cmdsize
) != sizeof(uuid_command
)) 
 183                         MacOSError::throwMe(errSecCSSignatureInvalid
); 
 184                 char result
[4 + sizeof(uuidc
->uuid
)]; 
 185                 memcpy(result
, "UUID", 4); 
 186                 memcpy(result
+4, uuidc
->uuid
, sizeof(uuidc
->uuid
)); 
 187                 return makeCFData(result
, sizeof(result
)); 
 190         // otherwise, use the SHA-1 hash of the entire load command area (this is way, way obsolete) 
 192         hash(&macho
->header(), sizeof(mach_header
)); 
 193         hash(macho
->loadCommands(), macho
->commandLength()); 
 196         return makeCFData(digest
, sizeof(digest
)); 
 201 // Retrieve a component from the executable. 
 202 // This reads the entire signing SuperBlob when first called for an executable, 
 203 // and then caches it for further use. 
 204 // Note that we could read individual components directly off disk and only cache 
 205 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected 
 206 // to cache the pieces anyway. 
 208 CFDataRef 
MachORep::component(CodeDirectory::SpecialSlot slot
) 
 214                 return embeddedComponent(slot
); 
 219 // Retrieve a component from the embedded signature SuperBlob (if present). 
 220 // This reads the entire signing SuperBlob when first called for an executable, 
 221 // and then caches it for further use. 
 222 // Note that we could read individual components directly off disk and only cache 
 223 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected 
 224 // to cache the pieces anyway. But it's not clear that the resulting multiple I/O 
 225 // calls wouldn't be slower in the end. 
 227 CFDataRef 
MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot
) 
 229         if (!mSigningData
) {            // fetch and cache 
 230                 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture()); 
 232                         if (const linkedit_data_command 
*cs 
= macho
->findCodeSignature()) { 
 233                                 size_t offset 
= macho
->flip(cs
->dataoff
); 
 234                                 size_t length 
= macho
->flip(cs
->datasize
); 
 235                                 if ((mSigningData 
= EmbeddedSignatureBlob::readBlob(macho
->fd(), macho
->offset() + offset
, length
))) { 
 236                                         secinfo("machorep", "%zd signing bytes in %d blob(s) from %s(%s)", 
 237                                                 mSigningData
->length(), mSigningData
->count(), 
 238                                                 mainExecutablePath().c_str(), macho
->architecture().name()); 
 240                                         secinfo("machorep", "failed to read signing bytes from %s(%s)", 
 241                                                 mainExecutablePath().c_str(), macho
->architecture().name()); 
 242                                         MacOSError::throwMe(errSecCSSignatureInvalid
); 
 247                 return mSigningData
->component(slot
); 
 255 // Extract an embedded Info.plist from the file. 
 256 // Returns NULL if none is found. 
 258 CFDataRef 
MachORep::infoPlist() 
 260         CFRef
<CFDataRef
> info
; 
 262                 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture()); 
 263                 if (const section 
*sect 
= macho
->findSection("__TEXT", "__info_plist")) { 
 265                                 const section_64 
*sect64 
= reinterpret_cast<const section_64 
*>(sect
); 
 266                                 info
.take(macho
->dataAt(macho
->flip(sect64
->offset
), (size_t)macho
->flip(sect64
->size
))); 
 268                                 info
.take(macho
->dataAt(macho
->flip(sect
->offset
), macho
->flip(sect
->size
))); 
 272                 secinfo("machorep", "exception reading embedded Info.plist"); 
 279 // Provide a (vaguely) human readable characterization of this code 
 281 string 
MachORep::format() 
 283         if (Universal 
*fat 
= mainExecutableImage()) { 
 284                 Universal::Architectures archs
; 
 285                 fat
->architectures(archs
); 
 286                 if (fat
->isUniversal()) { 
 287                         string s 
= "Mach-O universal ("; 
 288                         for (Universal::Architectures::const_iterator it 
= archs
.begin(); 
 289                                         it 
!= archs
.end(); ++it
) { 
 290                                 if (it 
!= archs
.begin()) 
 292                                 s 
+= it
->displayName(); 
 296                         assert(archs
.size() == 1); 
 297                         return string("Mach-O thin (") + archs
.begin()->displayName() + ")"; 
 300                 return "Mach-O (unrecognized format)"; 
 307 void MachORep::flush() 
 309         size_t offset 
= mExecutable
->offset(); 
 310         size_t length 
= mExecutable
->length(); 
 313         ::free(mSigningData
); 
 315         SingleDiskRep::flush(); 
 316         mExecutable 
= new Universal(fd(), offset
, length
); 
 319 CFDictionaryRef 
MachORep::diskRepInformation() 
 321     auto_ptr
<MachO
> macho (mainExecutableImage()->architecture()); 
 322     CFRef
<CFDictionaryRef
> info
; 
 324     if (const version_min_command 
*version 
= macho
->findMinVersion()) { 
 326         info
.take(cfmake
<CFMutableDictionaryRef
>("{%O = %d,%O = %d,%O = %d}", 
 327                                               kSecCodeInfoDiskRepOSPlatform
, macho
->flip(version
->cmd
), 
 328                                               kSecCodeInfoDiskRepOSVersionMin
, macho
->flip(version
->version
), 
 329                                               kSecCodeInfoDiskRepOSSDKVersion
, macho
->flip(version
->sdk
))); 
 331         if (macho
->flip(version
->cmd
) == LC_VERSION_MIN_MACOSX 
&& 
 332             macho
->flip(version
->sdk
) < (10 << 16 | 9 << 8)) 
 334             info
.take(cfmake
<CFMutableDictionaryRef
>("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}", 
 336                                                   kSecCodeInfoDiskRepNoLibraryValidation
)); 
 345 // Return a recommended unique identifier. 
 346 // If our file has an embedded Info.plist, use the CFBundleIdentifier from that. 
 347 // Otherwise, use the default. 
 349 string 
MachORep::recommendedIdentifier(const SigningContext 
&ctx
) 
 351         if (CFDataRef info 
= infoPlist()) { 
 352                 if (CFRef
<CFDictionaryRef
> dict 
= makeCFDictionaryFrom(info
)) { 
 353                         CFStringRef code 
= CFStringRef(CFDictionaryGetValue(dict
, kCFBundleIdentifierKey
)); 
 354                         if (code 
&& CFGetTypeID(code
) != CFStringGetTypeID()) 
 355                                 MacOSError::throwMe(errSecCSBadDictionaryFormat
); 
 357                                 return cfString(code
); 
 359                         MacOSError::throwMe(errSecCSBadDictionaryFormat
); 
 362         // ah well. Use the default 
 363         return SingleDiskRep::recommendedIdentifier(ctx
); 
 368 // The default suggested requirements for Mach-O binaries are as follows: 
 369 // Library requirement: Composed from dynamic load commands. 
 371 const Requirements 
*MachORep::defaultRequirements(const Architecture 
*arch
, const SigningContext 
&ctx
) 
 373         assert(arch
);           // enforced by signing infrastructure 
 374         Requirements::Maker maker
; 
 376         // add library requirements from DYLIB commands (if any) 
 377         if (Requirement 
*libreq 
= libraryRequirements(arch
, ctx
)) 
 378                 maker
.add(kSecLibraryRequirementType
, libreq
);  // takes ownership 
 384 Requirement 
*MachORep::libraryRequirements(const Architecture 
*arch
, const SigningContext 
&ctx
) 
 386         auto_ptr
<MachO
> macho(mainExecutableImage()->architecture(*arch
)); 
 387         Requirement::Maker maker
; 
 388         Requirement::Maker::Chain 
chain(maker
, opOr
); 
 391                 if (const linkedit_data_command 
*ldep 
= macho
->findLibraryDependencies()) { 
 392                         size_t offset 
= macho
->flip(ldep
->dataoff
); 
 393                         size_t length 
= macho
->flip(ldep
->datasize
); 
 394                         if (LibraryDependencyBlob 
*deplist 
= LibraryDependencyBlob::readBlob(macho
->fd(), macho
->offset() + offset
, length
)) { 
 396                                         secinfo("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)", 
 397                                                 deplist
->length(), deplist
->count(), 
 398                                                 mainExecutablePath().c_str(), macho
->architecture().name()); 
 399                                         unsigned count 
= deplist
->count(); 
 400                                         // we could walk through DYLIB load commands in parallel. We just don't need anything from them so far 
 401                                         for (unsigned n 
= 0; n 
< count
; n
++) { 
 402                                                 const Requirement 
*req 
= NULL
; 
 403                                                 if (const BlobCore 
*dep 
= deplist
->blob(n
)) { 
 404                                                         if ((req 
= Requirement::specific(dep
))) { 
 405                                                                 // binary code requirement; good to go 
 406                                                         } else if (const BlobWrapper 
*wrap 
= BlobWrapper::specific(dep
)) { 
 407                                                                 // blob-wrapped text form - convert to binary requirement 
 408                                                                 std::string reqString 
= std::string((const char *)wrap
->data(), wrap
->length()); 
 409                                                                 CFRef
<SecRequirementRef
> areq
; 
 410                                                                 MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString
), kSecCSDefaultFlags
, &areq
.aref())); 
 411                                                                 CFRef
<CFDataRef
> reqData
; 
 412                                                                 MacOSError::check(SecRequirementCopyData(areq
, kSecCSDefaultFlags
, &reqData
.aref())); 
 413                                                                 req 
= Requirement::specific((const BlobCore 
*)CFDataGetBytePtr(reqData
)); 
 415                                                                 secinfo("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep
->magic(), n
); 
 421                                                         secinfo("machorep", "missing DR info for library index %d", n
); 
 438 // Default to system page size for segmented (paged) signatures 
 440 size_t MachORep::pageSize(const SigningContext 
&) 
 442         return segmentedPageSize
; 
 449 void MachORep::strictValidate(const CodeDirectory
* cd
, const ToleratedErrors
& tolerated
, SecCSFlags flags
) 
 451         SingleDiskRep::strictValidate(cd
, tolerated
, flags
); 
 453         // if the constructor found suspicious issues, fail a struct validation now 
 454         if (mExecutable
->isSuspicious() && tolerated
.find(errSecCSBadMainExecutable
) == tolerated
.end()) 
 455                 MacOSError::throwMe(errSecCSBadMainExecutable
); 
 460 // FileDiskRep::Writers 
 462 DiskRep::Writer 
*MachORep::writer() 
 464         return new Writer(this); 
 469 // Write a component. 
 470 // MachORep::Writers don't write to components directly; the signing code uses special 
 471 // knowledge of the Mach-O format to build embedded signatures and blasts them directly 
 472 // to disk. Thus this implementation will never be called (and, if called, will simply fail). 
 474 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
) 
 477     Syslog::notice("code signing internal error: trying to write Mach-O component directly"); 
 478         MacOSError::throwMe(errSecCSInternalError
); 
 482 } // end namespace CodeSigning 
 483 } // end namespace Security