2 * Copyright (c) 2006-2010 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::HashAlgorithm hashType
, 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
, hashType
);
90 ArchEditor::~ArchEditor()
92 for (ArchMap::iterator it
= begin(); it
!= end(); ++it
)
100 BlobEditor::BlobEditor(Universal
&fat
, SecCodeSigner::Signer
&s
)
101 : ArchEditor(fat
, s
.digestAlgorithm(), 0), signer(s
)
105 void BlobEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
107 mGlobal
.component(slot
, data
);
110 void BlobEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
112 mMaker
.add(arch
.architecture
.cpuType(), blob
);
116 void BlobEditor::commit()
118 // create the architecture-global blob and store it into the superblob
119 mMaker
.add(0, mGlobal
.make()); // takes ownership of blob
121 // finish up the superblob and deliver it
122 DetachedSignatureBlob
*blob
= mMaker
.make();
123 signer
.state
.returnDetachedSignature(blob
, signer
);
129 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
130 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
131 // After the tool succeeds, we open the new file and are ready to write it.
133 MachOEditor::MachOEditor(DiskRep::Writer
*w
, Universal
&code
, CodeDirectory::HashAlgorithm hashType
, std::string srcPath
)
134 : ArchEditor(code
, hashType
, w
->attributes()),
137 tempPath(srcPath
+ ".cstemp"),
141 if (const char *path
= getenv(helperOverride
)) {
143 mHelperOverridden
= true;
145 mHelperPath
= helperPath
;
146 mHelperOverridden
= false;
150 MachOEditor::~MachOEditor()
154 ::remove(tempPath
.c_str()); // ignore error (can't do anything about it)
159 void MachOEditor::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
161 writer
->component(slot
, data
);
165 void MachOEditor::allocate()
167 // note that we may have a temporary file from now on (for cleanup in the error case)
168 mTempMayExist
= true;
170 // run codesign_allocate to make room in the executable file
173 if (!Child::succeeded())
174 MacOSError::throwMe(errSecCSHelperFailed
);
176 // open the new (temporary) Universal file
179 mFd
.open(tempPath
, O_RDWR
);
181 mNewCode
= new Universal(mFd
);
184 static const unsigned char appleReq
[] = {
185 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
186 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
187 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
188 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
190 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
191 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
194 void MachOEditor::parentAction()
196 if (mHelperOverridden
) {
197 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath
, this->pid());
198 // check code identity of an overridden allocation helper
199 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(mHelperPath
));
200 code
->staticValidate(kSecCSDefaultFlags
, NULL
);
201 code
->validateRequirement((const Requirement
*)appleReq
, errSecCSReqFailed
);
205 void MachOEditor::childAction()
207 vector
<const char *> arguments
;
208 arguments
.push_back(helperName
);
209 arguments
.push_back("-i");
210 arguments
.push_back(sourcePath
.c_str());
211 arguments
.push_back("-o");
212 arguments
.push_back(tempPath
.c_str());
214 for (Iterator it
= architecture
.begin(); it
!= architecture
.end(); ++it
) {
215 size_t size
= LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
);
216 char *ssize
; // we'll leak this (execv is coming soon)
217 asprintf(&ssize
, "%zd", size
);
219 if (const char *arch
= it
->first
.name()) {
220 CODESIGN_ALLOCATE_ARCH((char*)arch
, (unsigned int)size
);
221 arguments
.push_back("-a");
222 arguments
.push_back(arch
);
224 CODESIGN_ALLOCATE_ARCHN(it
->first
.cpuType(), it
->first
.cpuSubtype(), (unsigned int)size
);
225 arguments
.push_back("-A");
227 asprintf(&anum
, "%d", it
->first
.cpuType());
228 arguments
.push_back(anum
);
229 asprintf(&anum
, "%d", it
->first
.cpuSubtype());
230 arguments
.push_back(anum
);
232 arguments
.push_back(ssize
);
234 arguments
.push_back(NULL
);
236 if (mHelperOverridden
)
237 ::csops(0, CS_EXEC_SET_KILL
, NULL
, 0); // force code integrity
238 ::seteuid(0); // activate privilege if caller has it; ignore error if not
239 execv(mHelperPath
, (char * const *)&arguments
[0]);
242 void MachOEditor::reset(Arch
&arch
)
244 arch
.source
.reset(mNewCode
->architecture(arch
.architecture
));
245 arch
.cdbuilder
.reopen(tempPath
,
246 arch
.source
->offset(), arch
.source
->signingOffset());
251 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
252 // of the executable image file.
254 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
256 if (size_t offset
= arch
.source
->signingOffset()) {
257 size_t signingLength
= arch
.source
->signingLength();
258 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, (unsigned)blob
->length(), (unsigned)signingLength
);
259 if (signingLength
< blob
->length())
260 MacOSError::throwMe(errSecCSCMSTooLarge
);
261 arch
.source
->seek(offset
);
262 arch
.source
->writeAll(*blob
);
263 ::free(blob
); // done with it
265 secdebug("signer", "%p cannot find CODESIGNING section", this);
266 MacOSError::throwMe(errSecCSInternalError
);
273 // This moves the temporary editor copy over the source image file.
274 // Note that the Universal object returned by allocate() is still open
275 // and valid; the caller owns it.
277 void MachOEditor::commit()
279 // if the file's owned by someone else *and* we can become root...
281 UnixError::check(::stat(sourcePath
.c_str(), &st
));
283 // copy over all the *other* stuff
286 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
288 // perform copy under root or file-owner privileges if available
290 if (!guard
.seteuid(0))
291 guard
.seteuid(st
.st_uid
);
293 // copy metadata from original file...
294 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
296 // ... but explicitly update the timestamps since we did change the file
298 mFd
.read(&buf
, sizeof(buf
), 0);
299 mFd
.write(&buf
, sizeof(buf
), 0);
301 // move the new file into place
302 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
303 mTempMayExist
= false; // we renamed it away
309 // InternalRequirements
311 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
, const Requirement::Context
&context
)
313 // first add the default internal requirements
315 this->add(defaulted
);
316 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
319 // now override them with any requirements explicitly given by the signer
323 // now add the Designated Requirement, if we can make it and it's not been provided
324 if (!this->contains(kSecDesignatedRequirementType
)) {
325 DRMaker
maker(context
);
326 if (Requirement
*dr
= maker
.make()) {
327 this->add(kSecDesignatedRequirementType
, dr
); // takes ownership of dr
332 mReqs
= this->make();
337 // Pre-Signing contexts
339 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer
&signer
)
341 // construct a cert chain
342 if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) {
343 CFRef
<SecCertificateRef
> signingCert
;
344 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref()));
345 CFRef
<SecPolicyRef
> policy
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
);
346 CFRef
<SecTrustRef
> trust
;
347 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref()));
348 SecTrustResultType result
;
349 MacOSError::check(SecTrustEvaluate(trust
, &result
));
350 CSSM_TP_APPLE_EVIDENCE_INFO
*info
;
351 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
));
352 this->certs
= mCerts
;
356 this->identifier
= signer
.signingIdentifier();
360 } // end namespace CodeSigning
361 } // end namespace Security