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 /* Signature editing may have no need for cd builders, and not
266 * have opened them, so only reopen them conditionally. */
267 if (builder
.opened()) {
268 builder
.reopen(tempPath
, arch
.source
->offset(), arch
.source
->signingOffset());
276 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
277 // of the executable image file.
279 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
281 if (size_t offset
= arch
.source
->signingOffset()) {
282 size_t signingLength
= arch
.source
->signingLength();
283 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, (unsigned)blob
->length(), (unsigned)signingLength
);
284 if (signingLength
< blob
->length())
285 MacOSError::throwMe(errSecCSCMSTooLarge
);
286 arch
.source
->seek(offset
);
287 arch
.source
->writeAll(*blob
);
288 ::free(blob
); // done with it
290 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
291 MacOSError::throwMe(errSecCSInternalError
);
298 // This moves the temporary editor copy over the source image file.
299 // Note that the Universal object returned by allocate() is still open
300 // and valid; the caller owns it.
302 void MachOEditor::commit()
304 // if the file's owned by someone else *and* we can become root...
306 UnixError::check(::stat(sourcePath
.c_str(), &st
));
308 // copy over all the *other* stuff
311 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
313 // perform copy under root or file-owner privileges if available
315 if (!guard
.seteuid(0))
316 (void)guard
.seteuid(st
.st_uid
);
318 // copy metadata from original file...
319 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
322 // determine AFSC status if we are told to preserve compression
323 bool conductCompression
= false;
325 if (writer
->getPreserveAFSC()) {
326 if (queryCompressionInfo(sourcePath
.c_str(), &cInfo
) == 0) {
327 if (cInfo
.compressionType
!= 0 && cInfo
.compressedSize
> 0)
328 conductCompression
= true;
333 // ... but explicitly update the timestamps since we did change the file
335 mFd
.read(&buf
, sizeof(buf
), 0);
336 mFd
.write(&buf
, sizeof(buf
), 0);
338 // move the new file into place
339 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
340 mTempMayExist
= false; // we renamed it away
343 // if the original file was compressed, compress the new file after move
344 if (conductCompression
) {
345 CFMutableDictionaryRef options
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
346 CFStringRef val
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), cInfo
.compressionType
);
347 CFDictionarySetValue(options
, kAFSCCompressionTypes
, val
);
350 CompressionQueueContext compressionQueue
= CreateCompressionQueue(NULL
, NULL
, NULL
, NULL
, options
);
352 if (!CompressFile(compressionQueue
, sourcePath
.c_str(), NULL
)) {
353 secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath
.c_str());
354 MacOSError::throwMe(errSecCSInternalError
);
356 FinishCompressionAndCleanUp(compressionQueue
);
358 compressionQueue
= NULL
;
364 this->writer
->flush();
369 // InternalRequirements
371 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
, const Requirement::Context
&context
)
373 // first add the default internal requirements
375 this->add(defaulted
);
376 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
379 // now override them with any requirements explicitly given by the signer
383 // now add the Designated Requirement, if we can make it and it's not been provided
384 if (!this->contains(kSecDesignatedRequirementType
)) {
385 DRMaker
maker(context
);
386 if (Requirement
*dr
= maker
.make()) {
387 this->add(kSecDesignatedRequirementType
, dr
); // takes ownership of dr
392 mReqs
= this->make();
397 // Pre-Signing contexts
399 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer
&signer
)
401 // construct a cert chain
402 if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) {
403 CFRef
<SecCertificateRef
> signingCert
;
404 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref()));
405 CFRef
<SecPolicyRef
> policy
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
);
406 CFRef
<SecTrustRef
> trust
;
407 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref()));
408 SecTrustResultType result
;
409 MacOSError::check(SecTrustEvaluate(trust
, &result
));
410 CSSM_TP_APPLE_EVIDENCE_INFO
*info
;
411 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
));
412 this->certs
= mCerts
;
416 this->identifier
= signer
.signingIdentifier();
421 // A collector of CodeDirectories for hash-agile construction of signatures.
423 CodeDirectorySet::~CodeDirectorySet()
425 for (auto it
= begin(); it
!= end(); ++it
)
426 ::free(const_cast<CodeDirectory
*>(it
->second
));
430 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory
*cd
)
432 insert(make_pair(cd
->hashType
, cd
));
433 if (cd
->hashType
== kSecCodeSignatureHashSHA1
)
438 void CodeDirectorySet::populate(DiskRep::Writer
*writer
) const
442 if (mPrimary
== NULL
) // didn't add SHA-1; pick another occupant for this slot
443 mPrimary
= begin()->second
;
445 // reserve slot zero for a SHA-1 digest if present; else pick something else
446 CodeDirectory::SpecialSlot nextAlternate
= cdAlternateCodeDirectorySlots
;
447 for (auto it
= begin(); it
!= end(); ++it
) {
448 if (it
->second
== mPrimary
) {
449 writer
->codeDirectory(it
->second
, cdCodeDirectorySlot
);
451 writer
->codeDirectory(it
->second
, nextAlternate
++);
457 const CodeDirectory
* CodeDirectorySet::primary() const
459 if (mPrimary
== NULL
)
460 mPrimary
= begin()->second
;
464 CFArrayRef
CodeDirectorySet::hashList() const
466 CFRef
<CFMutableArrayRef
> hashList
= makeCFMutableArray(0);
467 for (auto it
= begin(); it
!= end(); ++it
) {
468 CFRef
<CFDataRef
> cdhash
= it
->second
->cdhash(true);
469 CFArrayAppendValue(hashList
, cdhash
);
471 return hashList
.yield();
474 CFDictionaryRef
CodeDirectorySet::hashDict() const
476 CFRef
<CFMutableDictionaryRef
> hashDict
= makeCFMutableDictionary();
478 for (auto it
= begin(); it
!= end(); ++it
) {
479 SECOidTag tag
= CodeDirectorySet::SECOidTagForAlgorithm(it
->first
);
481 if (tag
== SEC_OID_UNKNOWN
) {
482 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm
);
485 CFRef
<CFNumberRef
> hashType
= makeCFNumber(int(tag
));
486 CFRef
<CFDataRef
> fullCdhash
= it
->second
->cdhash(false); // Full-length cdhash!
487 CFDictionarySetValue(hashDict
, hashType
, fullCdhash
);
490 return hashDict
.yield();
493 SECOidTag
CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm
) {
497 case kSecCodeSignatureHashSHA1
:
500 case kSecCodeSignatureHashSHA256
:
501 case kSecCodeSignatureHashSHA256Truncated
: // truncated *page* hashes, not cdhash
502 tag
= SEC_OID_SHA256
;
504 case kSecCodeSignatureHashSHA384
:
505 tag
= SEC_OID_SHA384
;
508 tag
= SEC_OID_UNKNOWN
;
516 } // end namespace CodeSigning
517 } // end namespace Security