1/*
2 * Copyright (c) 2007 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// cfmdiskrep - single-file CFM (PEF) executable disk representation
26//
27#include "cfmdiskrep.h"
28#include <cstring>
29
30
31namespace Security {
32namespace CodeSigning {
33
34using namespace UnixPlusPlus;
35
36
37//
38// Everything's lazy in here
39//
40CFMDiskRep::CFMDiskRep(const char *path)
41	: SingleDiskRep(path), mTriedRead(false)
42{
43	CODESIGN_DISKREP_CREATE_CFM(this, (char*)path);
44}
45
46CFMDiskRep::~CFMDiskRep()
47{
48	if (mTriedRead)
49		delete mSigningData;
50}
51
52
53//
54// CFM filter heuristic.
55// We look for the PEF header within the first scanLength bytes
56// of the file's data fork, at certain alignment boundaries (probably
57// conservative).
58//
59bool CFMDiskRep::candidate(FileDesc &fd)
60{
61	static const char magicMarker[] = "Joy!peffpwpc";
62	static const size_t magicLength = 12;
63	static const size_t scanLength = 128;
64	static const size_t scanAlignment = 4;
65
66	char marker[scanLength];
67	if (fd.read(marker, scanLength, 0) == scanLength)
68		for (size_t p = 0; p <= scanLength - magicLength; p += scanAlignment)
69			if (!memcmp(marker+p, magicMarker, magicLength))
70				return true;
71	return false;
72}
73
74
75//
76// Extract and return a component by slot number.
77// If we have a Mach-O binary, use embedded components.
78// Otherwise, look for and return the extended attribute, if any.
79//
80CFDataRef CFMDiskRep::component(CodeDirectory::SpecialSlot slot)
81{
82	if (!mTriedRead)
83		readSigningData();
84	if (mSigningData)
85		return mSigningData->component(slot);
86	else
87		return NULL;
88}
89
90
91//
92// The signing limit is the start of the signature if present,
93// or the end of the file otherwise.
94//
95size_t CFMDiskRep::signingLimit()
96{
97	readSigningData();
98	if (mSigningData)
99		return mSigningOffset;
100	else
101		return fd().fileSize();
102}
103
104
105//
106// Various other aspects of our DiskRep personality.
107//
108string CFMDiskRep::format()
109{
110	return "CFM/PEF binary";
111}
112
113
114//
115// Discard cached information
116//
117void CFMDiskRep::flush()
118{
119	mTriedRead = false;
120	::free(mSigningData);
121}
122
123
124//
125// In Mac OS X, a CFM binary must always be managed by the LaunchCFMApp
126// system tool. Thus, we recommend that this be required as a host.
127//
128static const uint8_t cfm_ireqs[] = {	// host => anchor apple and identifier com.apple.LaunchCFMApp
129	0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
130	0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01,
131	0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16,
132	0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68,
133	0x43, 0x46, 0x4d, 0x41, 0x70, 0x70, 0x00, 0x00,
134};
135
136const Requirements *CFMDiskRep::defaultRequirements(const Architecture *, const SigningContext &)
137{
138	return ((const Requirements *)cfm_ireqs)->clone();	// need to pass ownership
139}
140
141
142//
143// Default to system-paged signing
144//
145size_t CFMDiskRep::pageSize(const SigningContext &)
146{
147	return segmentedPageSize;
148}
149
150
151//
152// Locate, read, and cache embedded signing data from the CFM binary.
153//
154void CFMDiskRep::readSigningData()
155{
156	if (!mTriedRead) {				// try it once
157		mSigningData = NULL;		// preset failure
158		mTriedRead = true;			// we've tried (and perhaps failed)
159
160		FileDesc &fd = this->fd();
161		fd.seek(fd.fileSize() - sizeof(Sentinel));
162		Sentinel sentinel;
163		if (fd.read(&sentinel, sizeof(sentinel), fd.fileSize() - sizeof(Sentinel)) == sizeof(Sentinel))
164			if (sentinel.magic == EmbeddedSignatureBlob::typeMagic) {
165				mSigningOffset = sentinel.offset;
166				if ((mSigningData = EmbeddedSignatureBlob::readBlob(fd, mSigningOffset)))
167					secdebug("cfmrep", "%zd signing bytes in %d blob(s) from %s(CFM)",
168						mSigningData->length(), mSigningData->count(),
169						mainExecutablePath().c_str());
170				else
171					secdebug("cfmrep", "failed to read signing bytes from %s(CFM)",
172						mainExecutablePath().c_str());
173			}
174	}
175}
176
177
178//
179// CFMDiskRep::Writers
180//
181DiskRep::Writer *CFMDiskRep::writer()
182{
183	return new Writer(this);
184}
185
186CFMDiskRep::Writer::~Writer()
187{
188	delete mSigningData;
189}
190
191
192//
193// Write a component.
194// Note that this isn't concerned with Mach-O writing; this is handled at
195// a much higher level. If we're called, it's extended attribute time.
196//
197void CFMDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
198{
199	EmbeddedSignatureBlob::Maker::component(slot, data);
200}
201
202
203//
204// Append the superblob we built to the CFM binary.
205// Note: Aligning the signing blob to a 16-byte boundary is not strictly necessary,
206// but it's what the Mach-O case does, and it probably improves performance a bit.
207//
208void CFMDiskRep::Writer::flush()
209{
210	delete mSigningData;			// ditch previous blob just in case
211	mSigningData = Maker::make();	// assemble new signature SuperBlob
212	size_t start = LowLevelMemoryUtilities::alignUp(rep->signingLimit(), 16);
213	Sentinel sentinel;
214	sentinel.magic = EmbeddedSignatureBlob::typeMagic;
215	sentinel.offset = (uint32_t)start;
216	AutoFileDesc fd(rep->path(), O_RDWR);
217	fd.seek(start);
218	fd.writeAll(mSigningData, mSigningData->length());
219	fd.writeAll(&sentinel, sizeof(sentinel));
220}
221
222
223} // end namespace CodeSigning
224} // end namespace Security
225