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