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>
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(makeCFData(*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 UnixError::throwMe(ENOEXEC
); //@@@ how to signal "it din' work"?
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
->validateDirectory();
201 code
->validateExecutable();
202 code
->validateResources();
203 code
->validateRequirement((const Requirement
*)appleReq
, errSecCSReqFailed
);
207 void MachOEditor::childAction()
209 vector
<const char *> arguments
;
210 arguments
.push_back(helperName
);
211 arguments
.push_back("-i");
212 arguments
.push_back(sourcePath
.c_str());
213 arguments
.push_back("-o");
214 arguments
.push_back(tempPath
.c_str());
216 for (Iterator it
= architecture
.begin(); it
!= architecture
.end(); ++it
) {
217 size_t size
= LowLevelMemoryUtilities::alignUp(it
->second
->blobSize
, csAlign
);
218 char *ssize
; // we'll leak this (execv is coming soon)
219 asprintf(&ssize
, "%zd", size
);
221 if (const char *arch
= it
->first
.name()) {
222 CODESIGN_ALLOCATE_ARCH((char*)arch
, size
);
223 arguments
.push_back("-a");
224 arguments
.push_back(arch
);
226 CODESIGN_ALLOCATE_ARCHN(it
->first
.cpuType(), it
->first
.cpuSubtype(), size
);
227 arguments
.push_back("-A");
229 asprintf(&anum
, "%d", it
->first
.cpuType());
230 arguments
.push_back(anum
);
231 asprintf(&anum
, "%d", it
->first
.cpuSubtype());
232 arguments
.push_back(anum
);
234 arguments
.push_back(ssize
);
236 arguments
.push_back(NULL
);
238 if (mHelperOverridden
)
239 ::csops(0, CS_EXEC_SET_KILL
, NULL
, 0); // force code integrity
240 ::seteuid(0); // activate privilege if caller has it; ignore error if not
241 execv(mHelperPath
, (char * const *)&arguments
[0]);
244 void MachOEditor::reset(Arch
&arch
)
246 arch
.source
.reset(mNewCode
->architecture(arch
.architecture
));
247 arch
.cdbuilder
.reopen(tempPath
,
248 arch
.source
->offset(), arch
.source
->signingOffset());
253 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
254 // of the executable image file.
256 void MachOEditor::write(Arch
&arch
, EmbeddedSignatureBlob
*blob
)
258 if (size_t offset
= arch
.source
->signingOffset()) {
259 size_t signingLength
= arch
.source
->signingLength();
260 CODESIGN_ALLOCATE_WRITE((char*)arch
.architecture
.name(), offset
, blob
->length(), signingLength
);
261 if (signingLength
< blob
->length())
262 MacOSError::throwMe(errSecCSCMSTooLarge
);
263 arch
.source
->seek(offset
);
264 arch
.source
->writeAll(*blob
);
265 ::free(blob
); // done with it
267 secdebug("signer", "%p cannot find CODESIGNING section", this);
268 MacOSError::throwMe(errSecCSInternalError
);
275 // This moves the temporary editor copy over the source image file.
276 // Note that the Universal object returned by allocate() is still open
277 // and valid; the caller owns it.
279 void MachOEditor::commit()
281 // if the file's owned by someone else *and* we can become root...
283 UnixError::check(::stat(sourcePath
.c_str(), &st
));
285 // copy over all the *other* stuff
288 copy
.set(COPYFILE_STATE_DST_FD
, &fd
);
290 // perform copy under root or file-owner privileges if available
292 if (!guard
.seteuid(0))
293 guard
.seteuid(st
.st_uid
);
295 // copy metadata from original file...
296 copy(sourcePath
.c_str(), NULL
, COPYFILE_SECURITY
| COPYFILE_METADATA
);
298 // ... but explicitly update the timestamps since we did change the file
300 mFd
.read(&buf
, sizeof(buf
), 0);
301 mFd
.write(&buf
, sizeof(buf
), 0);
303 // move the new file into place
304 UnixError::check(::rename(tempPath
.c_str(), sourcePath
.c_str()));
305 mTempMayExist
= false; // we renamed it away
311 // InternalRequirements
313 void InternalRequirements::operator () (const Requirements
*given
, const Requirements
*defaulted
, const Requirement::Context
&context
)
315 // first add the default internal requirements
317 this->add(defaulted
);
318 ::free((void *)defaulted
); // was malloc(3)ed by DiskRep
321 // now override them with any requirements explicitly given by the signer
325 // now add the Designated Requirement, if we can make it and it's not been provided
326 if (!this->contains(kSecDesignatedRequirementType
)) {
327 DRMaker
maker(context
);
328 if (Requirement
*dr
= maker
.make()) {
329 this->add(kSecDesignatedRequirementType
, dr
); // takes ownership of dr
334 mReqs
= this->make();
339 // Pre-Signing contexts
341 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer
&signer
)
343 // construct a cert chain
344 if (signer
.signingIdentity() != SecIdentityRef(kCFNull
)) {
345 CFRef
<SecCertificateRef
> signingCert
;
346 MacOSError::check(SecIdentityCopyCertificate(signer
.signingIdentity(), &signingCert
.aref()));
347 CFRef
<SecPolicyRef
> policy
= SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning
);
348 CFRef
<SecTrustRef
> trust
;
349 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert
.get()), policy
, &trust
.aref()));
350 SecTrustResultType result
;
351 MacOSError::check(SecTrustEvaluate(trust
, &result
));
352 CSSM_TP_APPLE_EVIDENCE_INFO
*info
;
353 MacOSError::check(SecTrustGetResult(trust
, &result
, &mCerts
.aref(), &info
));
354 this->certs
= mCerts
;
358 this->identifier
= signer
.signingIdentifier();
362 } // end namespace CodeSigning
363 } // end namespace Security