]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/cfmdiskrep.cpp
49edf08d012079e802a83d935226af512dc9fdd4
[apple/libsecurity_codesigning.git] / lib / cfmdiskrep.cpp
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
31 namespace Security {
32 namespace CodeSigning {
33
34 using namespace UnixPlusPlus;
35
36
37 //
38 // Everything's lazy in here
39 //
40 CFMDiskRep::CFMDiskRep(const char *path)
41 : SingleDiskRep(path), mTriedRead(false)
42 {
43 CODESIGN_DISKREP_CREATE_CFM(this, (char*)path);
44 }
45
46 CFMDiskRep::~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 //
59 bool 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 //
80 CFDataRef 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 // In Mac OS X, a CFM binary must always be managed by the LaunchCFMApp
93 // system tool. Thus, we recommend that this be required as a host.
94 //
95 static const uint8_t cfm_ireqs[] = { // host => anchor apple and identifier com.apple.LaunchCFMApp
96 0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
97 0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01,
98 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16,
99 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68,
100 0x43, 0x46, 0x4d, 0x41, 0x70, 0x70, 0x00, 0x00,
101 };
102
103 const Requirements *CFMDiskRep::defaultRequirements(const Architecture *)
104 {
105 return ((const Requirements *)cfm_ireqs)->clone(); // need to pass ownership
106 }
107
108
109 //
110 // Default to system-paged signing
111 //
112 size_t CFMDiskRep::pageSize()
113 {
114 return segmentedPageSize;
115 }
116
117
118 //
119 // The signing limit is the start of the signature if present,
120 // or the end of the file otherwise.
121 //
122 size_t CFMDiskRep::signingLimit()
123 {
124 readSigningData();
125 if (mSigningData)
126 return mSigningOffset;
127 else
128 return fd().fileSize();
129 }
130
131
132 //
133 // Various other aspects of our DiskRep personality.
134 //
135 string CFMDiskRep::format()
136 {
137 return "CFM/PEF binary";
138 }
139
140
141 //
142 // Discard cached information
143 //
144 void CFMDiskRep::flush()
145 {
146 mTriedRead = false;
147 ::free(mSigningData);
148 }
149
150
151 //
152 // Locate, read, and cache embedded signing data from the CFM binary.
153 //
154 void 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 //
181 DiskRep::Writer *CFMDiskRep::writer()
182 {
183 return new Writer(this);
184 }
185
186 CFMDiskRep::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 //
197 void 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 //
208 void 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 = 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