1/*
2 * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// signerutils - utilities for signature generation
26//
27#include "signerutils.h"
28#include "signer.h"
29#include "SecCodeSigner.h"
30#include <Security/SecIdentity.h>
31#include <Security/CMSEncoder.h>
32#include "resources.h"
33#include "csutilities.h"
34#include "drmaker.h"
35#include <security_utilities/unix++.h>
36#include <security_utilities/unixchild.h>
37#include <vector>
38
39// for helper validation
40#include "Code.h"
41#include <security_utilities/cfmunge.h>
42#include <sys/codesign.h>
43
44
45namespace Security {
46namespace CodeSigning {
47
48
49//
50// About the Mach-O allocation helper
51//
52static const char helperName[] = "codesign_allocate";
53static const char helperPath[] = "/usr/bin/codesign_allocate";
54static const char helperOverride[] = "CODESIGN_ALLOCATE";
55static const size_t csAlign = 16;
56
57
58//
59// BlobWriters
60//
61void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
62{
63	return EmbeddedSignatureBlob::Maker::component(slot, data);
64}
65
66
67void DetachedBlobWriter::flush()
68{
69	EmbeddedSignatureBlob *blob = this->make();
70	signer.code->detachedSignature(CFTempData(*blob));
71	signer.state.returnDetachedSignature(blob, signer);
72	::free(blob);
73}
74
75
76//
77// ArchEditor
78//
79ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, uint32_t attrs)
80	: DiskRep::Writer(attrs)
81{
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);
87}
88
89
90ArchEditor::~ArchEditor()
91{
92	for (ArchMap::iterator it = begin(); it != end(); ++it)
93		delete it->second;
94}
95
96
97//
98// BlobEditor
99//
100BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
101	: ArchEditor(fat, s.digestAlgorithm(), 0), signer(s)
102{ }
103
104
105void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
106{
107	mGlobal.component(slot, data);
108}
109
110void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
111{
112	mMaker.add(arch.architecture.cpuType(), blob);
113}
114
115
116void BlobEditor::commit()
117{
118	// create the architecture-global blob and store it into the superblob
119	mMaker.add(0, mGlobal.make());	// takes ownership of blob
120
121	// finish up the superblob and deliver it
122	DetachedSignatureBlob *blob = mMaker.make();
123	signer.state.returnDetachedSignature(blob, signer);
124	::free(blob);
125}
126
127
128//
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.
132//
133MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithm hashType, std::string srcPath)
134	: ArchEditor(code, hashType, w->attributes()),
135	  writer(w),
136	  sourcePath(srcPath),
137	  tempPath(srcPath + ".cstemp"),
138	  mNewCode(NULL),
139	  mTempMayExist(false)
140{
141	if (const char *path = getenv(helperOverride)) {
142		mHelperPath = path;
143		mHelperOverridden = true;
144	} else {
145		mHelperPath = helperPath;
146		mHelperOverridden = false;
147	}
148}
149
150MachOEditor::~MachOEditor()
151{
152	delete mNewCode;
153	if (mTempMayExist)
154		::remove(tempPath.c_str());		// ignore error (can't do anything about it)
155	this->kill();
156}
157
158
159void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
160{
161	writer->component(slot, data);
162}
163
164
165void MachOEditor::allocate()
166{
167	// note that we may have a temporary file from now on (for cleanup in the error case)
168	mTempMayExist = true;
169
170	// run codesign_allocate to make room in the executable file
171	fork();
172	wait();
173	if (!Child::succeeded())
174		MacOSError::throwMe(errSecCSHelperFailed);
175
176	// open the new (temporary) Universal file
177	{
178		UidGuard guard(0);
179		mFd.open(tempPath, O_RDWR);
180	}
181	mNewCode = new Universal(mFd);
182}
183
184static 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,
192};
193
194void MachOEditor::parentAction()
195{
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);
202	}
203}
204
205void MachOEditor::childAction()
206{
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());
213
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);
218
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);
223		} else {
224			CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size);
225			arguments.push_back("-A");
226			char *anum;
227			asprintf(&anum, "%d", it->first.cpuType());
228			arguments.push_back(anum);
229			asprintf(&anum, "%d", it->first.cpuSubtype());
230			arguments.push_back(anum);
231		}
232		arguments.push_back(ssize);
233	}
234	arguments.push_back(NULL);
235
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]);
240}
241
242void MachOEditor::reset(Arch &arch)
243{
244	arch.source.reset(mNewCode->architecture(arch.architecture));
245	arch.cdbuilder.reopen(tempPath,
246		arch.source->offset(), arch.source->signingOffset());
247}
248
249
250//
251// MachOEditor's write() method actually writes the blob into the CODESIGNING section
252// of the executable image file.
253//
254void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
255{
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
264	} else {
265		secdebug("signer", "%p cannot find CODESIGNING section", this);
266		MacOSError::throwMe(errSecCSInternalError);
267	}
268}
269
270
271//
272// Commit the edit.
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.
276//
277void MachOEditor::commit()
278{
279	// if the file's owned by someone else *and* we can become root...
280	struct stat st;
281	UnixError::check(::stat(sourcePath.c_str(), &st));
282
283	// copy over all the *other* stuff
284	Copyfile copy;
285	int fd = mFd;
286	copy.set(COPYFILE_STATE_DST_FD, &fd);
287	{
288		// perform copy under root or file-owner privileges if available
289		UidGuard guard;
290		if (!guard.seteuid(0))
291			guard.seteuid(st.st_uid);
292
293		// copy metadata from original file...
294		copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
295
296		// ... but explicitly update the timestamps since we did change the file
297		char buf;
298		mFd.read(&buf, sizeof(buf), 0);
299		mFd.write(&buf, sizeof(buf), 0);
300
301		// move the new file into place
302		UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
303		mTempMayExist = false;		// we renamed it away
304	}
305}
306
307
308//
309// InternalRequirements
310//
311void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
312{
313	// first add the default internal requirements
314	if (defaulted) {
315		this->add(defaulted);
316		::free((void *)defaulted);		// was malloc(3)ed by DiskRep
317	}
318
319	// now override them with any requirements explicitly given by the signer
320	if (given)
321		this->add(given);
322
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
328		}
329	}
330
331	// return the result
332	mReqs = this->make();
333}
334
335
336//
337// Pre-Signing contexts
338//
339PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
340{
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;
353	}
354
355	// other stuff
356	this->identifier = signer.signingIdentifier();
357}
358
359
360} // end namespace CodeSigning
361} // end namespace Security
362