]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signerutils.cpp
c270ed22cb7327c029b57c2af1de6d3aeebb5f41
[apple/libsecurity_codesigning.git] / lib / signerutils.cpp
1 /*
2 * Copyright (c) 2006-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 // 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 "renum.h"
33 #include <security_utilities/unix++.h>
34 #include <security_utilities/unixchild.h>
35 #include <vector>
36
37 // for helper validation
38 #include "Code.h"
39 #include "cfmunge.h"
40 #include <sys/codesign.h>
41
42
43 namespace Security {
44 namespace CodeSigning {
45
46
47 //
48 // About the Mach-O allocation helper
49 //
50 static const char helperName[] = "codesign_allocate";
51 static const char helperPath[] = "/usr/bin/codesign_allocate";
52 static const char helperOverride[] = "CODESIGN_ALLOCATE";
53 static const size_t csAlign = 16;
54
55
56 //
57 // InternalRequirements
58 //
59 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted)
60 {
61 if (defaulted) {
62 this->add(defaulted);
63 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
64 }
65 if (given)
66 this->add(given);
67 mReqs = make();
68 }
69
70
71 //
72 // BlobWriters
73 //
74 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
75 {
76 return EmbeddedSignatureBlob::Maker::component(slot, data);
77 }
78
79
80 void DetachedBlobWriter::flush()
81 {
82 EmbeddedSignatureBlob *blob = this->make();
83 signer.code->detachedSignature(CFTempData(*blob));
84 signer.state.returnDetachedSignature(blob);
85 ::free(blob);
86 }
87
88
89 //
90 // ArchEditor
91 //
92 ArchEditor::ArchEditor(Universal &code, uint32_t attrs /* = 0 */)
93 : DiskRep::Writer(attrs)
94 {
95 Universal::Architectures archList;
96 code.architectures(archList);
97 for (Universal::Architectures::const_iterator it = archList.begin();
98 it != archList.end(); ++it)
99 architecture[*it] = new Arch(*it);
100 }
101
102
103 ArchEditor::~ArchEditor()
104 {
105 for (ArchMap::iterator it = begin(); it != end(); ++it)
106 delete it->second;
107 }
108
109
110 //
111 // BlobEditor
112 //
113 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
114 {
115 mGlobal.component(slot, data);
116 }
117
118 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
119 {
120 mMaker.add(arch.architecture.cpuType(), blob);
121 }
122
123
124 void BlobEditor::commit()
125 {
126 // create the architecture-global blob and store it into the superblob
127 mMaker.add(0, mGlobal.make()); // takes ownership of blob
128
129 // finish up the superblob and deliver it
130 DetachedSignatureBlob *blob = mMaker.make();
131 signer.state.returnDetachedSignature(blob);
132 ::free(blob);
133 }
134
135
136 //
137 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
138 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
139 // After the tool succeeds, we open the new file and are ready to write it.
140 //
141 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath)
142 : ArchEditor(code, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"),
143 mNewCode(NULL), mTempMayExist(false)
144 {
145 if (const char *path = getenv(helperOverride)) {
146 mHelperPath = path;
147 mHelperOverridden = true;
148 } else {
149 mHelperPath = helperPath;
150 mHelperOverridden = false;
151 }
152 }
153
154 MachOEditor::~MachOEditor()
155 {
156 delete mNewCode;
157 if (mTempMayExist)
158 ::remove(tempPath.c_str()); // ignore error (can't do anything about it)
159
160 //@@@ this code should be in UnixChild::kill() -- migrate it there
161 if (state() == alive) {
162 this->kill(SIGTERM); // shoot it once
163 checkChildren(); // check for quick death
164 if (state() == alive) {
165 usleep(500000); // give it some grace
166 if (state() == alive) { // could have been reaped by another thread
167 checkChildren(); // check again
168 if (state() == alive) { // it... just... won't... die...
169 this->kill(SIGKILL); // take THAT!
170 checkChildren();
171 if (state() == alive) // stuck zombie
172 abandon(); // leave the body behind
173 }
174 }
175 }
176 }
177 }
178
179
180 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
181 {
182 writer->component(slot, data);
183 }
184
185
186 void MachOEditor::allocate()
187 {
188 // note that we may have a temporary file from now on (for cleanup in the error case)
189 mTempMayExist = true;
190
191 // run codesign_allocate to make room in the executable file
192 fork();
193 wait();
194 if (!Child::succeeded())
195 UnixError::throwMe(ENOEXEC); //@@@ how to signal "it din' work"?
196
197 // open the new (temporary) Universal file
198 {
199 UidGuard guard(0);
200 mFd.open(tempPath, O_RDWR);
201 }
202 mNewCode = new Universal(mFd);
203 }
204
205 static const unsigned char appleReq[] = { // anchor apple
206 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
207 };
208
209 void MachOEditor::parentAction()
210 {
211 if (mHelperOverridden) {
212 secdebug("machoedit", "validating alternate codesign_allocate at %s (pid=%d)", mHelperPath, this->pid());
213 // check code identity of an overridden allocation helper
214 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
215 code->validateDirectory();
216 code->validateExecutable();
217 code->validateResources();
218 code->validateRequirements((const Requirement *)appleReq, errSecCSReqFailed);
219 }
220 }
221
222 void MachOEditor::childAction()
223 {
224 vector<const char *> arguments;
225 arguments.push_back(helperName);
226 arguments.push_back("-i");
227 arguments.push_back(sourcePath.c_str());
228 arguments.push_back("-o");
229 arguments.push_back(tempPath.c_str());
230
231 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
232 char *size; // we'll leak this (execv is coming soon)
233 asprintf(&size, "%d", LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign));
234 secdebug("machoedit", "preparing %s size=%s", it->first.name(), size);
235
236 if (const char *arch = it->first.name()) {
237 arguments.push_back("-a");
238 arguments.push_back(arch);
239 } else {
240 arguments.push_back("-A");
241 char *anum;
242 asprintf(&anum, "%d", it->first.cpuType());
243 arguments.push_back(anum);
244 asprintf(&anum, "%d", it->first.cpuSubtype());
245 arguments.push_back(anum);
246 }
247 arguments.push_back(size);
248 }
249 arguments.push_back(NULL);
250
251 if (mHelperOverridden)
252 ::csops(0, CS_EXEC_SET_KILL, NULL, 0); // force code integrity
253 ::seteuid(0); // activate privilege if caller has it; ignore error if not
254 execv(mHelperPath, (char * const *)&arguments[0]);
255 }
256
257 void MachOEditor::reset(Arch &arch)
258 {
259 arch.source.reset(mNewCode->architecture(arch.architecture));
260 arch.cdbuilder.reopen(tempPath,
261 arch.source->offset(), arch.source->signingOffset());
262 }
263
264
265 //
266 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
267 // of the executable image file.
268 //
269 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
270 {
271 if (size_t offset = arch.source->signingOffset()) {
272 size_t signingLength = arch.source->signingLength();
273 secdebug("codesign", "writing architecture %s at 0x%zx (%zd of %zd)",
274 arch.architecture.name(), offset, blob->length(), signingLength);
275 if (signingLength < blob->length()) {
276 secdebug("codesign", "trying to write %zd bytes into %zd area",
277 blob->length(), signingLength);
278 MacOSError::throwMe(errSecCSInternalError);
279 }
280 arch.source->seek(offset);
281 arch.source->writeAll(*blob);
282 ::free(blob); // done with it
283 } else {
284 secdebug("signer", "%p cannot find CODESIGNING section", this);
285 MacOSError::throwMe(errSecCSInternalError);
286 }
287 }
288
289
290 //
291 // Commit the edit.
292 // This moves the temporary editor copy over the source image file.
293 // Note that the Universal object returned by allocate() is still open
294 // and valid; the caller owns it.
295 //
296 void MachOEditor::commit()
297 {
298 // if the file's owned by someone else *and* we can become root...
299 struct stat st;
300 UnixError::check(::stat(sourcePath.c_str(), &st));
301
302 // copy over all the *other* stuff
303 Copyfile copy;
304 int fd = mFd;
305 copy.set(COPYFILE_STATE_DST_FD, &fd);
306 {
307 // perform copy under root or file-owner privileges if available
308 UidGuard guard;
309 if (!guard.seteuid(0))
310 guard.seteuid(st.st_uid);
311 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
312
313 // move the new file into place
314 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
315 mTempMayExist = false; // we renamed it away
316 }
317 }
318
319
320 //
321 // Copyfile
322 //
323 Copyfile::Copyfile()
324 {
325 if (!(mState = copyfile_state_alloc()))
326 UnixError::throwMe();
327 }
328
329 void Copyfile::set(uint32_t flag, const void *value)
330 {
331 check(::copyfile_state_set(mState, flag, value));
332 }
333
334 void Copyfile::get(uint32_t flag, void *value)
335 {
336 check(::copyfile_state_set(mState, flag, value));
337 }
338
339 void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags)
340 {
341 check(::copyfile(src, dst, mState, flags));
342 }
343
344 void Copyfile::check(int rc)
345 {
346 if (rc < 0)
347 UnixError::throwMe();
348 }
349
350
351 } // end namespace CodeSigning
352 } // end namespace Security