2  * Copyright (c) 2006-2013 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 // signerutils - utilities for signature generation 
  27 #include "csutilities.h" 
  29 #include "resources.h" 
  30 #include "signerutils.h" 
  33 #include <Security/SecCmsBase.h> 
  34 #include <Security/SecIdentity.h> 
  35 #include <Security/CMSEncoder.h> 
  37 #include "SecCodeSigner.h" 
  39 #include <security_utilities/unix++.h> 
  40 #include <security_utilities/logging.h> 
  41 #include <security_utilities/unixchild.h> 
  45 // for helper validation 
  47 #include <security_utilities/cfmunge.h> 
  48 #include <sys/codesign.h> 
  52 namespace CodeSigning 
{ 
  56 // About the Mach-O allocation helper 
  58 static const char helperName
[] = "codesign_allocate"; 
  59 static const char helperPath
[] = "/usr/bin/codesign_allocate"; 
  60 static const char helperOverride
[] = "CODESIGN_ALLOCATE"; 
  61 static const size_t csAlign 
= 16; 
  67 void BlobWriter::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
) 
  69         return EmbeddedSignatureBlob::Maker::component(slot
, data
); 
  73 void DetachedBlobWriter::flush() 
  75         EmbeddedSignatureBlob 
*blob 
= this->make(); 
  76         signer
.code
->detachedSignature(CFTempData(*blob
)); 
  77         signer
.state
.returnDetachedSignature(blob
, signer
); 
  85 ArchEditor::ArchEditor(Universal 
&code
, CodeDirectory::HashAlgorithms hashTypes
, uint32_t attrs
) 
  86         : DiskRep::Writer(attrs
) 
  88         Universal::Architectures archList
; 
  89         code
.architectures(archList
); 
  90         for (Universal::Architectures::const_iterator it 
= archList
.begin(); 
  91                         it 
!= archList
.end(); ++it
) 
  92                 architecture
[*it
] = new Arch(*it
, hashTypes
); 
  96 ArchEditor::~ArchEditor() 
  98         for (ArchMap::iterator it 
= begin(); it 
!= end(); ++it
) 
 103 ArchEditor::Arch::Arch(const Architecture 
&arch
, CodeDirectory::HashAlgorithms hashTypes
) 
 106         for (auto type 
= hashTypes
.begin(); type 
!= hashTypes
.end(); ++type
) 
 107                 cdBuilders
.insert(make_pair(*type
, new CodeDirectory::Builder(*type
))); 
 114 BlobEditor::BlobEditor(Universal 
&fat
, SecCodeSigner::Signer 
&s
) 
 115         : ArchEditor(fat
, s
.digestAlgorithms(), 0), signer(s
) 
 119 void BlobEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
) 
 121         mGlobal
.component(slot
, data
); 
 124 void BlobEditor::write(Arch 
&arch
, EmbeddedSignatureBlob 
*blob
) 
 126         mMaker
.add(arch
.architecture
.cpuType(), blob
); 
 130 void BlobEditor::commit() 
 132         // create the architecture-global blob and store it into the superblob 
 133         mMaker
.add(0, mGlobal
.make());  // takes ownership of blob 
 135         // finish up the superblob and deliver it 
 136         DetachedSignatureBlob 
*blob 
= mMaker
.make(); 
 137         signer
.state
.returnDetachedSignature(blob
, signer
); 
 143 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to 
 144 // "drill up" the Mach-O binary for insertion of Code Signing signature data. 
 145 // After the tool succeeds, we open the new file and are ready to write it. 
 147 MachOEditor::MachOEditor(DiskRep::Writer 
*w
, Universal 
&code
, CodeDirectory::HashAlgorithms hashTypes
, std::string srcPath
) 
 148         : ArchEditor(code
, hashTypes
, w
->attributes()), 
 151           tempPath(srcPath 
+ ".cstemp"), 
 152           mHashTypes(hashTypes
), 
 156         if (const char *path 
= getenv(helperOverride
)) { 
 158                 mHelperOverridden 
= true; 
 160                 mHelperPath 
= helperPath
; 
 161                 mHelperOverridden 
= false; 
 165 MachOEditor::~MachOEditor() 
 169                 ::remove(tempPath
.c_str());             // ignore error (can't do anything about it) 
 174 void MachOEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
) 
 176         writer
->component(slot
, data
); 
 180 void MachOEditor::allocate() 
 182         // note that we may have a temporary file from now on (for cleanup in the error case) 
 183         mTempMayExist 
= true; 
 185         // run codesign_allocate to make room in the executable file 
 188         if (!Child::succeeded()) 
 189                 MacOSError::throwMe(errSecCSHelperFailed
); 
 191         // open the new (temporary) Universal file 
 194                 mFd
.open(tempPath
, O_RDWR
); 
 196         mNewCode 
= new Universal(mFd
); 
 199 static const unsigned char appleReq
[] = { 
 200         // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate" 
 201         0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 
 202         0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c, 
 203         0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00, 
 204         0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 
 205         0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f, 
 206         0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 
 209 void MachOEditor::parentAction() 
 211         if (mHelperOverridden
) { 
 212                 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath
, this->pid()); 
 213                 // check code identity of an overridden allocation helper 
 214                 SecPointer
<SecStaticCode
> code 
= new SecStaticCode(DiskRep::bestGuess(mHelperPath
)); 
 215                 code
->staticValidate(kSecCSDefaultFlags
, NULL
); 
 216                 code
->validateRequirement((const Requirement 
*)appleReq
, errSecCSReqFailed
); 
 220 void MachOEditor::childAction() 
 222         vector
<const char *> arguments
; 
 223         arguments
.push_back(helperName
); 
 224         arguments
.push_back("-i"); 
 225         arguments
.push_back(sourcePath
.c_str()); 
 226         arguments
.push_back("-o"); 
 227         arguments
.push_back(tempPath
.c_str()); 
 229         for (Iterator it 
= architecture
.begin(); it 
!= architecture
.end(); ++it
) { 
 230                 size_t size 
= LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
); 
 231                 char *ssize
;                    // we'll leak this (execv is coming soon) 
 232                 asprintf(&ssize
, "%zd", size
); 
 234                 if (const char *arch 
= it
->first
.name()) { 
 235                         CODESIGN_ALLOCATE_ARCH((char*)arch
, (unsigned int)size
); 
 236                         arguments
.push_back("-a"); 
 237                         arguments
.push_back(arch
); 
 239                         CODESIGN_ALLOCATE_ARCHN(it
->first
.cpuType(), it
->first
.cpuSubtype(), (unsigned int)size
); 
 240                         arguments
.push_back("-A"); 
 242                         asprintf(&anum
, "%d", it
->first
.cpuType()); 
 243                         arguments
.push_back(anum
); 
 244                         asprintf(&anum
, "%d", it
->first
.cpuSubtype()); 
 245                         arguments
.push_back(anum
); 
 247                 arguments
.push_back(ssize
); 
 249         arguments
.push_back(NULL
); 
 251         if (mHelperOverridden
) 
 252                 ::csops(0, CS_OPS_MARKKILL
, NULL
, 0);           // force code integrity 
 253         (void)::seteuid(0);     // activate privilege if caller has it; ignore error if not 
 254         execv(mHelperPath
, (char * const *)&arguments
[0]); 
 257 void MachOEditor::reset(Arch 
&arch
) 
 259         arch
.source
.reset(mNewCode
->architecture(arch
.architecture
)); 
 261         for (auto type 
= mHashTypes
.begin(); type 
!= mHashTypes
.end(); ++type
) { 
 262                 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) { 
 263                         builder
.reopen(tempPath
, arch
.source
->offset(), arch
.source
->signingOffset()); 
 270 // MachOEditor's write() method actually writes the blob into the CODESIGNING section 
 271 // of the executable image file. 
 273 void MachOEditor::write(Arch 
&arch
, EmbeddedSignatureBlob 
*blob
) 
 275         if (size_t offset 
= arch
.source
->signingOffset()) { 
 276                 size_t signingLength 
= arch
.source
->signingLength(); 
 277                 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, (unsigned)blob
->length(), (unsigned)signingLength
); 
 278                 if (signingLength 
< blob
->length()) 
 279                         MacOSError::throwMe(errSecCSCMSTooLarge
); 
 280                 arch
.source
->seek(offset
); 
 281                 arch
.source
->writeAll(*blob
); 
 282                 ::free(blob
);           // done with it 
 284                 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this); 
 285                 MacOSError::throwMe(errSecCSInternalError
); 
 292 // This moves the temporary editor copy over the source image file. 
 293 // Note that the Universal object returned by allocate() is still open 
 294 // and valid; the caller owns it. 
 296 void MachOEditor::commit() 
 298         // if the file's owned by someone else *and* we can become root... 
 300         UnixError::check(::stat(sourcePath
.c_str(), &st
)); 
 302         // copy over all the *other* stuff 
 305         copy
.set(COPYFILE_STATE_DST_FD
, &fd
); 
 307                 // perform copy under root or file-owner privileges if available 
 309                 if (!guard
.seteuid(0)) 
 310                         (void)guard
.seteuid(st
.st_uid
); 
 312                 // copy metadata from original file... 
 313                 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY 
| COPYFILE_METADATA
); 
 315                 // ... but explicitly update the timestamps since we did change the file 
 317                 mFd
.read(&buf
, sizeof(buf
), 0); 
 318                 mFd
.write(&buf
, sizeof(buf
), 0); 
 320                 // move the new file into place 
 321                 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str())); 
 322                 mTempMayExist 
= false;          // we renamed it away 
 324         this->writer
->flush(); 
 329 // InternalRequirements 
 331 void InternalRequirements::operator () (const Requirements 
*given
, const Requirements 
*defaulted
, const Requirement::Context 
&context
) 
 333         // first add the default internal requirements 
 335                 this->add(defaulted
); 
 336                 ::free((void *)defaulted
);              // was malloc(3)ed by DiskRep 
 339         // now override them with any requirements explicitly given by the signer 
 343         // now add the Designated Requirement, if we can make it and it's not been provided 
 344         if (!this->contains(kSecDesignatedRequirementType
)) { 
 345                 DRMaker 
maker(context
); 
 346                 if (Requirement 
*dr 
= maker
.make()) { 
 347                         this->add(kSecDesignatedRequirementType
, dr
);           // takes ownership of dr 
 352         mReqs 
= this->make(); 
 357 // Pre-Signing contexts 
 359 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer 
&signer
) 
 361         // construct a cert chain 
 362         if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) { 
 363                 CFRef
<SecCertificateRef
> signingCert
; 
 364                 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref())); 
 365                 CFRef
<SecPolicyRef
> policy 
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
); 
 366                 CFRef
<SecTrustRef
> trust
; 
 367                 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref())); 
 368                 SecTrustResultType result
; 
 369                 MacOSError::check(SecTrustEvaluate(trust
, &result
)); 
 370                 CSSM_TP_APPLE_EVIDENCE_INFO 
*info
; 
 371                 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
)); 
 372                 this->certs 
= mCerts
; 
 376         this->identifier 
= signer
.signingIdentifier(); 
 381 // A collector of CodeDirectories for hash-agile construction of signatures. 
 383 CodeDirectorySet::~CodeDirectorySet() 
 385         for (auto it 
= begin(); it 
!= end(); ++it
) 
 386                 ::free(const_cast<CodeDirectory
*>(it
->second
)); 
 390 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory 
*cd
) 
 392         insert(make_pair(cd
->hashType
, cd
)); 
 393         if (cd
->hashType 
== kSecCodeSignatureHashSHA1
) 
 398 void CodeDirectorySet::populate(DiskRep::Writer 
*writer
) const 
 402         if (mPrimary 
== NULL
)   // didn't add SHA-1; pick another occupant for this slot 
 403                 mPrimary 
= begin()->second
; 
 405         // reserve slot zero for a SHA-1 digest if present; else pick something else 
 406         CodeDirectory::SpecialSlot nextAlternate 
= cdAlternateCodeDirectorySlots
; 
 407         for (auto it 
= begin(); it 
!= end(); ++it
) { 
 408                 if (it
->second 
== mPrimary
) { 
 409                         writer
->codeDirectory(it
->second
, cdCodeDirectorySlot
); 
 411                         writer
->codeDirectory(it
->second
, nextAlternate
++); 
 417 const CodeDirectory
* CodeDirectorySet::primary() const 
 419         if (mPrimary 
== NULL
) 
 420                 mPrimary 
= begin()->second
; 
 424 CFArrayRef 
CodeDirectorySet::hashList() const 
 426         CFRef
<CFMutableArrayRef
> hashList 
= makeCFMutableArray(0); 
 427         for (auto it 
= begin(); it 
!= end(); ++it
) { 
 428                 CFRef
<CFDataRef
> cdhash 
= it
->second
->cdhash(true); 
 429                 CFArrayAppendValue(hashList
, cdhash
); 
 431         return hashList
.yield(); 
 434 CFDictionaryRef 
CodeDirectorySet::hashDict() const 
 436         CFRef
<CFMutableDictionaryRef
> hashDict 
= makeCFMutableDictionary(); 
 438         for (auto it 
= begin(); it 
!= end(); ++it
) { 
 439                 SECOidTag tag 
= CodeDirectorySet::SECOidTagForAlgorithm(it
->first
); 
 441                 if (tag 
== SEC_OID_UNKNOWN
) { 
 442                         MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm
); 
 445                 CFRef
<CFNumberRef
> hashType 
= makeCFNumber(int(tag
)); 
 446                 CFRef
<CFDataRef
> fullCdhash 
= it
->second
->cdhash(false); // Full-length cdhash! 
 447                 CFDictionarySetValue(hashDict
, hashType
, fullCdhash
); 
 450         return hashDict
.yield(); 
 453 SECOidTag 
CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm
) { 
 457                 case kSecCodeSignatureHashSHA1
: 
 460                 case kSecCodeSignatureHashSHA256
: 
 461                 case kSecCodeSignatureHashSHA256Truncated
: // truncated *page* hashes, not cdhash 
 462                         tag 
= SEC_OID_SHA256
; 
 464                 case kSecCodeSignatureHashSHA384
: 
 465                         tag 
= SEC_OID_SHA384
; 
 468                         tag 
= SEC_OID_UNKNOWN
; 
 476 } // end namespace CodeSigning 
 477 } // end namespace Security