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
)
107 for (auto type
= hashTypes
.begin(); type
!= hashTypes
.end(); ++type
)
108 cdBuilders
.insert(make_pair(*type
, new CodeDirectory::Builder(*type
)));
115 BlobEditor::BlobEditor(Universal
&fat
, SecCodeSigner::Signer
&s
)
116 : ArchEditor(fat
, s
.digestAlgorithms(), 0), signer(s
)
120 void BlobEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
122 mGlobal
.component(slot
, data
);
125 void BlobEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
127 mMaker
.add(arch
.architecture
.cpuType(), blob
);
131 void BlobEditor::commit()
133 // create the architecture-global blob and store it into the superblob
134 mMaker
.add(0, mGlobal
.make()); // takes ownership of blob
136 // finish up the superblob and deliver it
137 DetachedSignatureBlob
*blob
= mMaker
.make();
138 signer
.state
.returnDetachedSignature(blob
, signer
);
144 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
145 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
146 // After the tool succeeds, we open the new file and are ready to write it.
148 MachOEditor::MachOEditor(DiskRep::Writer
*w
, Universal
&code
, CodeDirectory::HashAlgorithms hashTypes
, std::string srcPath
)
149 : ArchEditor(code
, hashTypes
, w
->attributes()),
152 tempPath(srcPath
+ ".cstemp"),
153 mHashTypes(hashTypes
),
157 if (const char *path
= getenv(helperOverride
)) {
159 mHelperOverridden
= true;
161 mHelperPath
= helperPath
;
162 mHelperOverridden
= false;
166 MachOEditor::~MachOEditor()
170 ::remove(tempPath
.c_str()); // ignore error (can't do anything about it)
176 void MachOEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
178 writer
->component(slot
, data
);
182 void MachOEditor::allocate()
184 // note that we may have a temporary file from now on (for cleanup in the error case)
185 mTempMayExist
= true;
187 // run codesign_allocate to make room in the executable file
190 if (!Child::succeeded())
191 MacOSError::throwMe(errSecCSHelperFailed
);
193 // open the new (temporary) Universal file
196 mFd
.open(tempPath
, O_RDWR
);
198 mNewCode
= new Universal(mFd
);
201 static const unsigned char appleReq
[] = {
202 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
203 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
204 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
205 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
206 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
207 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
208 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
211 void MachOEditor::parentAction()
213 if (mHelperOverridden
) {
214 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath
, this->pid());
215 // check code identity of an overridden allocation helper
216 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(mHelperPath
));
217 code
->staticValidate(kSecCSDefaultFlags
, NULL
);
218 code
->validateRequirement((const Requirement
*)appleReq
, errSecCSReqFailed
);
222 void MachOEditor::childAction()
224 vector
<const char *> arguments
;
225 arguments
.push_back(helperName
);
226 arguments
.push_back("-i");
227 arguments
.push_back(sourcePath
.c_str());
228 arguments
.push_back("-o");
229 arguments
.push_back(tempPath
.c_str());
231 for (Iterator it
= architecture
.begin(); it
!= architecture
.end(); ++it
) {
232 size_t size
= LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
);
233 char *ssize
; // we'll leak this (execv is coming soon)
234 asprintf(&ssize
, "%zd", size
);
236 if (const char *arch
= it
->first
.name()) {
237 CODESIGN_ALLOCATE_ARCH((char*)arch
, (unsigned int)size
);
238 arguments
.push_back("-a");
239 arguments
.push_back(arch
);
241 CODESIGN_ALLOCATE_ARCHN(it
->first
.cpuType(), it
->first
.cpuSubtype(), (unsigned int)size
);
242 arguments
.push_back("-A");
244 asprintf(&anum
, "%d", it
->first
.cpuType());
245 arguments
.push_back(anum
);
246 asprintf(&anum
, "%d", it
->first
.cpuSubtype());
247 arguments
.push_back(anum
);
249 arguments
.push_back(ssize
);
251 arguments
.push_back(NULL
);
253 if (mHelperOverridden
)
254 ::csops(0, CS_OPS_MARKKILL
, NULL
, 0); // force code integrity
255 (void)::seteuid(0); // activate privilege if caller has it; ignore error if not
256 execv(mHelperPath
, (char * const *)&arguments
[0]);
259 void MachOEditor::reset(Arch
&arch
)
261 arch
.source
.reset(mNewCode
->architecture(arch
.architecture
));
263 for (auto type
= mHashTypes
.begin(); type
!= mHashTypes
.end(); ++type
) {
264 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) {
265 builder
.reopen(tempPath
, arch
.source
->offset(), arch
.source
->signingOffset());
272 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
273 // of the executable image file.
275 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
277 if (size_t offset
= arch
.source
->signingOffset()) {
278 size_t signingLength
= arch
.source
->signingLength();
279 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, (unsigned)blob
->length(), (unsigned)signingLength
);
280 if (signingLength
< blob
->length())
281 MacOSError::throwMe(errSecCSCMSTooLarge
);
282 arch
.source
->seek(offset
);
283 arch
.source
->writeAll(*blob
);
284 ::free(blob
); // done with it
286 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
287 MacOSError::throwMe(errSecCSInternalError
);
294 // This moves the temporary editor copy over the source image file.
295 // Note that the Universal object returned by allocate() is still open
296 // and valid; the caller owns it.
298 void MachOEditor::commit()
300 // if the file's owned by someone else *and* we can become root...
302 UnixError::check(::stat(sourcePath
.c_str(), &st
));
304 // copy over all the *other* stuff
307 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
309 // perform copy under root or file-owner privileges if available
311 if (!guard
.seteuid(0))
312 (void)guard
.seteuid(st
.st_uid
);
314 // copy metadata from original file...
315 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
318 // determine AFSC status if we are told to preserve compression
319 bool conductCompression
= false;
321 if (writer
->getPreserveAFSC()) {
322 if (queryCompressionInfo(sourcePath
.c_str(), &cInfo
) == 0) {
323 if (cInfo
.compressionType
!= 0 && cInfo
.compressedSize
> 0)
324 conductCompression
= true;
329 // ... but explicitly update the timestamps since we did change the file
331 mFd
.read(&buf
, sizeof(buf
), 0);
332 mFd
.write(&buf
, sizeof(buf
), 0);
334 // move the new file into place
335 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
336 mTempMayExist
= false; // we renamed it away
339 // if the original file was compressed, compress the new file after move
340 if (conductCompression
) {
341 CFMutableDictionaryRef options
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
342 CFStringRef val
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), cInfo
.compressionType
);
343 CFDictionarySetValue(options
, kAFSCCompressionTypes
, val
);
346 CompressionQueueContext compressionQueue
= CreateCompressionQueue(NULL
, NULL
, NULL
, NULL
, options
);
348 if (!CompressFile(compressionQueue
, sourcePath
.c_str(), NULL
)) {
349 secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath
.c_str());
350 MacOSError::throwMe(errSecCSInternalError
);
352 FinishCompressionAndCleanUp(compressionQueue
);
354 compressionQueue
= NULL
;
360 this->writer
->flush();
365 // InternalRequirements
367 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
, const Requirement::Context
&context
)
369 // first add the default internal requirements
371 this->add(defaulted
);
372 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
375 // now override them with any requirements explicitly given by the signer
379 // now add the Designated Requirement, if we can make it and it's not been provided
380 if (!this->contains(kSecDesignatedRequirementType
)) {
381 DRMaker
maker(context
);
382 if (Requirement
*dr
= maker
.make()) {
383 this->add(kSecDesignatedRequirementType
, dr
); // takes ownership of dr
388 mReqs
= this->make();
393 // Pre-Signing contexts
395 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer
&signer
)
397 // construct a cert chain
398 if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) {
399 CFRef
<SecCertificateRef
> signingCert
;
400 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref()));
401 CFRef
<SecPolicyRef
> policy
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
);
402 CFRef
<SecTrustRef
> trust
;
403 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref()));
404 SecTrustResultType result
;
405 MacOSError::check(SecTrustEvaluate(trust
, &result
));
406 CSSM_TP_APPLE_EVIDENCE_INFO
*info
;
407 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
));
408 this->certs
= mCerts
;
412 this->identifier
= signer
.signingIdentifier();
417 // A collector of CodeDirectories for hash-agile construction of signatures.
419 CodeDirectorySet::~CodeDirectorySet()
421 for (auto it
= begin(); it
!= end(); ++it
)
422 ::free(const_cast<CodeDirectory
*>(it
->second
));
426 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory
*cd
)
428 insert(make_pair(cd
->hashType
, cd
));
429 if (cd
->hashType
== kSecCodeSignatureHashSHA1
)
434 void CodeDirectorySet::populate(DiskRep::Writer
*writer
) const
438 if (mPrimary
== NULL
) // didn't add SHA-1; pick another occupant for this slot
439 mPrimary
= begin()->second
;
441 // reserve slot zero for a SHA-1 digest if present; else pick something else
442 CodeDirectory::SpecialSlot nextAlternate
= cdAlternateCodeDirectorySlots
;
443 for (auto it
= begin(); it
!= end(); ++it
) {
444 if (it
->second
== mPrimary
) {
445 writer
->codeDirectory(it
->second
, cdCodeDirectorySlot
);
447 writer
->codeDirectory(it
->second
, nextAlternate
++);
453 const CodeDirectory
* CodeDirectorySet::primary() const
455 if (mPrimary
== NULL
)
456 mPrimary
= begin()->second
;
460 CFArrayRef
CodeDirectorySet::hashList() const
462 CFRef
<CFMutableArrayRef
> hashList
= makeCFMutableArray(0);
463 for (auto it
= begin(); it
!= end(); ++it
) {
464 CFRef
<CFDataRef
> cdhash
= it
->second
->cdhash(true);
465 CFArrayAppendValue(hashList
, cdhash
);
467 return hashList
.yield();
470 CFDictionaryRef
CodeDirectorySet::hashDict() const
472 CFRef
<CFMutableDictionaryRef
> hashDict
= makeCFMutableDictionary();
474 for (auto it
= begin(); it
!= end(); ++it
) {
475 SECOidTag tag
= CodeDirectorySet::SECOidTagForAlgorithm(it
->first
);
477 if (tag
== SEC_OID_UNKNOWN
) {
478 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm
);
481 CFRef
<CFNumberRef
> hashType
= makeCFNumber(int(tag
));
482 CFRef
<CFDataRef
> fullCdhash
= it
->second
->cdhash(false); // Full-length cdhash!
483 CFDictionarySetValue(hashDict
, hashType
, fullCdhash
);
486 return hashDict
.yield();
489 SECOidTag
CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm
) {
493 case kSecCodeSignatureHashSHA1
:
496 case kSecCodeSignatureHashSHA256
:
497 case kSecCodeSignatureHashSHA256Truncated
: // truncated *page* hashes, not cdhash
498 tag
= SEC_OID_SHA256
;
500 case kSecCodeSignatureHashSHA384
:
501 tag
= SEC_OID_SHA384
;
504 tag
= SEC_OID_UNKNOWN
;
512 } // end namespace CodeSigning
513 } // end namespace Security