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/logging.h>
37 #include <security_utilities/unixchild.h>
40 // for helper validation
42 #include <security_utilities/cfmunge.h>
43 #include <sys/codesign.h>
47 namespace CodeSigning
{
51 // About the Mach-O allocation helper
53 static const char helperName
[] = "codesign_allocate";
54 static const char helperPath
[] = "/usr/bin/codesign_allocate";
55 static const char helperOverride
[] = "CODESIGN_ALLOCATE";
56 static const size_t csAlign
= 16;
62 void BlobWriter::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
64 return EmbeddedSignatureBlob::Maker::component(slot
, data
);
68 void DetachedBlobWriter::flush()
70 EmbeddedSignatureBlob
*blob
= this->make();
71 signer
.code
->detachedSignature(CFTempData(*blob
));
72 signer
.state
.returnDetachedSignature(blob
, signer
);
80 ArchEditor::ArchEditor(Universal
&code
, CodeDirectory::HashAlgorithms hashTypes
, uint32_t attrs
)
81 : DiskRep::Writer(attrs
)
83 Universal::Architectures archList
;
84 code
.architectures(archList
);
85 for (Universal::Architectures::const_iterator it
= archList
.begin();
86 it
!= archList
.end(); ++it
)
87 architecture
[*it
] = new Arch(*it
, hashTypes
);
91 ArchEditor::~ArchEditor()
93 for (ArchMap::iterator it
= begin(); it
!= end(); ++it
)
98 ArchEditor::Arch::Arch(const Architecture
&arch
, CodeDirectory::HashAlgorithms hashTypes
)
101 for (auto type
= hashTypes
.begin(); type
!= hashTypes
.end(); ++type
)
102 cdBuilders
.insert(make_pair(*type
, new CodeDirectory::Builder(*type
)));
109 BlobEditor::BlobEditor(Universal
&fat
, SecCodeSigner::Signer
&s
)
110 : ArchEditor(fat
, s
.digestAlgorithms(), 0), signer(s
)
114 void BlobEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
116 mGlobal
.component(slot
, data
);
119 void BlobEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
121 mMaker
.add(arch
.architecture
.cpuType(), blob
);
125 void BlobEditor::commit()
127 // create the architecture-global blob and store it into the superblob
128 mMaker
.add(0, mGlobal
.make()); // takes ownership of blob
130 // finish up the superblob and deliver it
131 DetachedSignatureBlob
*blob
= mMaker
.make();
132 signer
.state
.returnDetachedSignature(blob
, signer
);
138 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
139 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
140 // After the tool succeeds, we open the new file and are ready to write it.
142 MachOEditor::MachOEditor(DiskRep::Writer
*w
, Universal
&code
, CodeDirectory::HashAlgorithms hashTypes
, std::string srcPath
)
143 : ArchEditor(code
, hashTypes
, w
->attributes()),
146 tempPath(srcPath
+ ".cstemp"),
147 mHashTypes(hashTypes
),
151 if (const char *path
= getenv(helperOverride
)) {
153 mHelperOverridden
= true;
155 mHelperPath
= helperPath
;
156 mHelperOverridden
= false;
160 MachOEditor::~MachOEditor()
164 ::remove(tempPath
.c_str()); // ignore error (can't do anything about it)
169 void MachOEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
171 writer
->component(slot
, data
);
175 void MachOEditor::allocate()
177 // note that we may have a temporary file from now on (for cleanup in the error case)
178 mTempMayExist
= true;
180 // run codesign_allocate to make room in the executable file
183 if (!Child::succeeded())
184 MacOSError::throwMe(errSecCSHelperFailed
);
186 // open the new (temporary) Universal file
189 mFd
.open(tempPath
, O_RDWR
);
191 mNewCode
= new Universal(mFd
);
194 static const unsigned char appleReq
[] = {
195 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
196 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
197 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
198 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
200 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
201 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
204 void MachOEditor::parentAction()
206 if (mHelperOverridden
) {
207 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath
, this->pid());
208 // check code identity of an overridden allocation helper
209 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(mHelperPath
));
210 code
->staticValidate(kSecCSDefaultFlags
, NULL
);
211 code
->validateRequirement((const Requirement
*)appleReq
, errSecCSReqFailed
);
215 void MachOEditor::childAction()
217 vector
<const char *> arguments
;
218 arguments
.push_back(helperName
);
219 arguments
.push_back("-i");
220 arguments
.push_back(sourcePath
.c_str());
221 arguments
.push_back("-o");
222 arguments
.push_back(tempPath
.c_str());
224 for (Iterator it
= architecture
.begin(); it
!= architecture
.end(); ++it
) {
225 size_t size
= LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
);
226 char *ssize
; // we'll leak this (execv is coming soon)
227 asprintf(&ssize
, "%zd", size
);
229 if (const char *arch
= it
->first
.name()) {
230 CODESIGN_ALLOCATE_ARCH((char*)arch
, (unsigned int)size
);
231 arguments
.push_back("-a");
232 arguments
.push_back(arch
);
234 CODESIGN_ALLOCATE_ARCHN(it
->first
.cpuType(), it
->first
.cpuSubtype(), (unsigned int)size
);
235 arguments
.push_back("-A");
237 asprintf(&anum
, "%d", it
->first
.cpuType());
238 arguments
.push_back(anum
);
239 asprintf(&anum
, "%d", it
->first
.cpuSubtype());
240 arguments
.push_back(anum
);
242 arguments
.push_back(ssize
);
244 arguments
.push_back(NULL
);
246 if (mHelperOverridden
)
247 ::csops(0, CS_OPS_MARKKILL
, NULL
, 0); // force code integrity
248 (void)::seteuid(0); // activate privilege if caller has it; ignore error if not
249 execv(mHelperPath
, (char * const *)&arguments
[0]);
252 void MachOEditor::reset(Arch
&arch
)
254 arch
.source
.reset(mNewCode
->architecture(arch
.architecture
));
256 for (auto type
= mHashTypes
.begin(); type
!= mHashTypes
.end(); ++type
) {
257 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) {
258 builder
.reopen(tempPath
, arch
.source
->offset(), arch
.source
->signingOffset());
265 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
266 // of the executable image file.
268 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
270 if (size_t offset
= arch
.source
->signingOffset()) {
271 size_t signingLength
= arch
.source
->signingLength();
272 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, (unsigned)blob
->length(), (unsigned)signingLength
);
273 if (signingLength
< blob
->length())
274 MacOSError::throwMe(errSecCSCMSTooLarge
);
275 arch
.source
->seek(offset
);
276 arch
.source
->writeAll(*blob
);
277 ::free(blob
); // done with it
279 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
280 MacOSError::throwMe(errSecCSInternalError
);
287 // This moves the temporary editor copy over the source image file.
288 // Note that the Universal object returned by allocate() is still open
289 // and valid; the caller owns it.
291 void MachOEditor::commit()
293 // if the file's owned by someone else *and* we can become root...
295 UnixError::check(::stat(sourcePath
.c_str(), &st
));
297 // copy over all the *other* stuff
300 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
302 // perform copy under root or file-owner privileges if available
304 if (!guard
.seteuid(0))
305 (void)guard
.seteuid(st
.st_uid
);
307 // copy metadata from original file...
308 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
310 // ... but explicitly update the timestamps since we did change the file
312 mFd
.read(&buf
, sizeof(buf
), 0);
313 mFd
.write(&buf
, sizeof(buf
), 0);
315 // move the new file into place
316 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
317 mTempMayExist
= false; // we renamed it away
319 this->writer
->flush();
324 // InternalRequirements
326 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
, const Requirement::Context
&context
)
328 // first add the default internal requirements
330 this->add(defaulted
);
331 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
334 // now override them with any requirements explicitly given by the signer
338 // now add the Designated Requirement, if we can make it and it's not been provided
339 if (!this->contains(kSecDesignatedRequirementType
)) {
340 DRMaker
maker(context
);
341 if (Requirement
*dr
= maker
.make()) {
342 this->add(kSecDesignatedRequirementType
, dr
); // takes ownership of dr
347 mReqs
= this->make();
352 // Pre-Signing contexts
354 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer
&signer
)
356 // construct a cert chain
357 if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) {
358 CFRef
<SecCertificateRef
> signingCert
;
359 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref()));
360 CFRef
<SecPolicyRef
> policy
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
);
361 CFRef
<SecTrustRef
> trust
;
362 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref()));
363 SecTrustResultType result
;
364 MacOSError::check(SecTrustEvaluate(trust
, &result
));
365 CSSM_TP_APPLE_EVIDENCE_INFO
*info
;
366 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
));
367 this->certs
= mCerts
;
371 this->identifier
= signer
.signingIdentifier();
376 // A collector of CodeDirectories for hash-agile construction of signatures.
378 CodeDirectorySet::~CodeDirectorySet()
380 for (auto it
= begin(); it
!= end(); ++it
)
381 ::free(const_cast<CodeDirectory
*>(it
->second
));
385 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory
*cd
)
387 insert(make_pair(cd
->hashType
, cd
));
388 if (cd
->hashType
== kSecCodeSignatureHashSHA1
)
393 void CodeDirectorySet::populate(DiskRep::Writer
*writer
) const
397 if (mPrimary
== NULL
) // didn't add SHA-1; pick another occupant for this slot
398 mPrimary
= begin()->second
;
400 // reserve slot zero for a SHA-1 digest if present; else pick something else
401 CodeDirectory::SpecialSlot nextAlternate
= cdAlternateCodeDirectorySlots
;
402 for (auto it
= begin(); it
!= end(); ++it
) {
403 if (it
->second
== mPrimary
) {
404 writer
->codeDirectory(it
->second
, cdCodeDirectorySlot
);
406 writer
->codeDirectory(it
->second
, nextAlternate
++);
412 const CodeDirectory
* CodeDirectorySet::primary() const
414 if (mPrimary
== NULL
)
415 mPrimary
= begin()->second
;
420 CFArrayRef
CodeDirectorySet::hashBag() const
422 CFRef
<CFMutableArrayRef
> hashList
= makeCFMutableArray(0);
423 for (auto it
= begin(); it
!= end(); ++it
) {
424 CFRef
<CFDataRef
> cdhash
= it
->second
->cdhash();
425 CFArrayAppendValue(hashList
, cdhash
);
427 return hashList
.yield();
431 } // end namespace CodeSigning
432 } // end namespace Security