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 "signerutils.h"
29 #include "SecCodeSigner.h"
30 #include <Security/SecIdentity.h>
31 #include <Security/CMSEncoder.h>
32 #include "resources.h"
33 #include "csutilities.h"
35 #include <security_utilities/unix++.h>
36 #include <security_utilities/unixchild.h>
39 // for helper validation
41 #include <security_utilities/cfmunge.h>
42 #include <sys/codesign.h>
46 namespace CodeSigning
{
50 // About the Mach-O allocation helper
52 static const char helperName
[] = "codesign_allocate";
53 static const char helperPath
[] = "/usr/bin/codesign_allocate";
54 static const char helperOverride
[] = "CODESIGN_ALLOCATE";
55 static const size_t csAlign
= 16;
61 void BlobWriter::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
63 return EmbeddedSignatureBlob::Maker::component(slot
, data
);
67 void DetachedBlobWriter::flush()
69 EmbeddedSignatureBlob
*blob
= this->make();
70 signer
.code
->detachedSignature(CFTempData(*blob
));
71 signer
.state
.returnDetachedSignature(blob
, signer
);
79 ArchEditor::ArchEditor(Universal
&code
, CodeDirectory::HashAlgorithms hashTypes
, uint32_t attrs
)
80 : DiskRep::Writer(attrs
)
82 Universal::Architectures archList
;
83 code
.architectures(archList
);
84 for (Universal::Architectures::const_iterator it
= archList
.begin();
85 it
!= archList
.end(); ++it
)
86 architecture
[*it
] = new Arch(*it
, hashTypes
);
90 ArchEditor::~ArchEditor()
92 for (ArchMap::iterator it
= begin(); it
!= end(); ++it
)
97 ArchEditor::Arch::Arch(const Architecture
&arch
, CodeDirectory::HashAlgorithms hashTypes
)
100 for (auto type
= hashTypes
.begin(); type
!= hashTypes
.end(); ++type
)
101 cdBuilders
.insert(make_pair(*type
, new CodeDirectory::Builder(*type
)));
108 BlobEditor::BlobEditor(Universal
&fat
, SecCodeSigner::Signer
&s
)
109 : ArchEditor(fat
, s
.digestAlgorithms(), 0), signer(s
)
113 void BlobEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
115 mGlobal
.component(slot
, data
);
118 void BlobEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
120 mMaker
.add(arch
.architecture
.cpuType(), blob
);
124 void BlobEditor::commit()
126 // create the architecture-global blob and store it into the superblob
127 mMaker
.add(0, mGlobal
.make()); // takes ownership of blob
129 // finish up the superblob and deliver it
130 DetachedSignatureBlob
*blob
= mMaker
.make();
131 signer
.state
.returnDetachedSignature(blob
, signer
);
137 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
138 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
139 // After the tool succeeds, we open the new file and are ready to write it.
141 MachOEditor::MachOEditor(DiskRep::Writer
*w
, Universal
&code
, CodeDirectory::HashAlgorithms hashTypes
, std::string srcPath
)
142 : ArchEditor(code
, hashTypes
, w
->attributes()),
145 tempPath(srcPath
+ ".cstemp"),
146 mHashTypes(hashTypes
),
150 if (const char *path
= getenv(helperOverride
)) {
152 mHelperOverridden
= true;
154 mHelperPath
= helperPath
;
155 mHelperOverridden
= false;
159 MachOEditor::~MachOEditor()
163 ::remove(tempPath
.c_str()); // ignore error (can't do anything about it)
168 void MachOEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
170 writer
->component(slot
, data
);
174 void MachOEditor::allocate()
176 // note that we may have a temporary file from now on (for cleanup in the error case)
177 mTempMayExist
= true;
179 // run codesign_allocate to make room in the executable file
182 if (!Child::succeeded())
183 MacOSError::throwMe(errSecCSHelperFailed
);
185 // open the new (temporary) Universal file
188 mFd
.open(tempPath
, O_RDWR
);
190 mNewCode
= new Universal(mFd
);
193 static const unsigned char appleReq
[] = {
194 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
195 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
196 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
197 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
198 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
199 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
200 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
203 void MachOEditor::parentAction()
205 if (mHelperOverridden
) {
206 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath
, this->pid());
207 // check code identity of an overridden allocation helper
208 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(mHelperPath
));
209 code
->staticValidate(kSecCSDefaultFlags
, NULL
);
210 code
->validateRequirement((const Requirement
*)appleReq
, errSecCSReqFailed
);
214 void MachOEditor::childAction()
216 vector
<const char *> arguments
;
217 arguments
.push_back(helperName
);
218 arguments
.push_back("-i");
219 arguments
.push_back(sourcePath
.c_str());
220 arguments
.push_back("-o");
221 arguments
.push_back(tempPath
.c_str());
223 for (Iterator it
= architecture
.begin(); it
!= architecture
.end(); ++it
) {
224 size_t size
= LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
);
225 char *ssize
; // we'll leak this (execv is coming soon)
226 asprintf(&ssize
, "%zd", size
);
228 if (const char *arch
= it
->first
.name()) {
229 CODESIGN_ALLOCATE_ARCH((char*)arch
, (unsigned int)size
);
230 arguments
.push_back("-a");
231 arguments
.push_back(arch
);
233 CODESIGN_ALLOCATE_ARCHN(it
->first
.cpuType(), it
->first
.cpuSubtype(), (unsigned int)size
);
234 arguments
.push_back("-A");
236 asprintf(&anum
, "%d", it
->first
.cpuType());
237 arguments
.push_back(anum
);
238 asprintf(&anum
, "%d", it
->first
.cpuSubtype());
239 arguments
.push_back(anum
);
241 arguments
.push_back(ssize
);
243 arguments
.push_back(NULL
);
245 if (mHelperOverridden
)
246 ::csops(0, CS_OPS_MARKKILL
, NULL
, 0); // force code integrity
247 ::seteuid(0); // activate privilege if caller has it; ignore error if not
248 execv(mHelperPath
, (char * const *)&arguments
[0]);
251 void MachOEditor::reset(Arch
&arch
)
253 arch
.source
.reset(mNewCode
->architecture(arch
.architecture
));
255 for (auto type
= mHashTypes
.begin(); type
!= mHashTypes
.end(); ++type
) {
256 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) {
257 builder
.reopen(tempPath
, arch
.source
->offset(), arch
.source
->signingOffset());
264 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
265 // of the executable image file.
267 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
269 if (size_t offset
= arch
.source
->signingOffset()) {
270 size_t signingLength
= arch
.source
->signingLength();
271 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, (unsigned)blob
->length(), (unsigned)signingLength
);
272 if (signingLength
< blob
->length())
273 MacOSError::throwMe(errSecCSCMSTooLarge
);
274 arch
.source
->seek(offset
);
275 arch
.source
->writeAll(*blob
);
276 ::free(blob
); // done with it
278 secdebug("signer", "%p cannot find CODESIGNING section", this);
279 MacOSError::throwMe(errSecCSInternalError
);
286 // This moves the temporary editor copy over the source image file.
287 // Note that the Universal object returned by allocate() is still open
288 // and valid; the caller owns it.
290 void MachOEditor::commit()
292 // if the file's owned by someone else *and* we can become root...
294 UnixError::check(::stat(sourcePath
.c_str(), &st
));
296 // copy over all the *other* stuff
299 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
301 // perform copy under root or file-owner privileges if available
303 if (!guard
.seteuid(0))
304 guard
.seteuid(st
.st_uid
);
306 // copy metadata from original file...
307 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
309 // ... but explicitly update the timestamps since we did change the file
311 mFd
.read(&buf
, sizeof(buf
), 0);
312 mFd
.write(&buf
, sizeof(buf
), 0);
314 // move the new file into place
315 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
316 mTempMayExist
= false; // we renamed it away
322 // InternalRequirements
324 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
, const Requirement::Context
&context
)
326 // first add the default internal requirements
328 this->add(defaulted
);
329 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
332 // now override them with any requirements explicitly given by the signer
336 // now add the Designated Requirement, if we can make it and it's not been provided
337 if (!this->contains(kSecDesignatedRequirementType
)) {
338 DRMaker
maker(context
);
339 if (Requirement
*dr
= maker
.make()) {
340 this->add(kSecDesignatedRequirementType
, dr
); // takes ownership of dr
345 mReqs
= this->make();
350 // Pre-Signing contexts
352 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer
&signer
)
354 // construct a cert chain
355 if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) {
356 CFRef
<SecCertificateRef
> signingCert
;
357 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref()));
358 CFRef
<SecPolicyRef
> policy
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
);
359 CFRef
<SecTrustRef
> trust
;
360 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref()));
361 SecTrustResultType result
;
362 MacOSError::check(SecTrustEvaluate(trust
, &result
));
363 CSSM_TP_APPLE_EVIDENCE_INFO
*info
;
364 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
));
365 this->certs
= mCerts
;
369 this->identifier
= signer
.signingIdentifier();
374 // A collector of CodeDirectories for hash-agile construction of signatures.
376 CodeDirectorySet::~CodeDirectorySet()
378 for (auto it
= begin(); it
!= end(); ++it
)
379 ::free(const_cast<CodeDirectory
*>(it
->second
));
383 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory
*cd
)
385 insert(make_pair(cd
->hashType
, cd
));
386 if (cd
->hashType
== kSecCodeSignatureHashSHA1
)
391 void CodeDirectorySet::populate(DiskRep::Writer
*writer
) const
395 if (mPrimary
== NULL
) // didn't add SHA-1; pick another occupant for this slot
396 mPrimary
= begin()->second
;
398 // reserve slot zero for a SHA-1 digest if present; else pick something else
399 CodeDirectory::SpecialSlot nextAlternate
= cdAlternateCodeDirectorySlots
;
400 for (auto it
= begin(); it
!= end(); ++it
) {
401 if (it
->second
== mPrimary
) {
402 writer
->codeDirectory(it
->second
, cdCodeDirectorySlot
);
404 writer
->codeDirectory(it
->second
, nextAlternate
++);
410 const CodeDirectory
* CodeDirectorySet::primary() const
412 if (mPrimary
== NULL
)
413 mPrimary
= begin()->second
;
418 CFArrayRef
CodeDirectorySet::hashBag() const
420 CFRef
<CFMutableArrayRef
> hashList
= makeCFMutableArray(0);
421 for (auto it
= begin(); it
!= end(); ++it
) {
422 CFRef
<CFDataRef
> cdhash
= it
->second
->cdhash();
423 CFArrayAppendValue(hashList
, cdhash
);
425 return hashList
.yield();
429 } // end namespace CodeSigning
430 } // end namespace Security