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