]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signerutils.cpp
e005e82700b6e4d2f6713cfa2e34eea25ede015a
[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 namespace Security {
38 namespace CodeSigning {
39
40
41 //
42 // About the Mach-O allocation helper
43 //
44 static const char helperName[] = "codesign_allocate";
45 static const char helperPath[] = "/usr/bin/codesign_allocate";
46 static const size_t csAlign = 16;
47
48
49 //
50 // InternalRequirements
51 //
52 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted)
53 {
54 if (defaulted) {
55 this->add(defaulted);
56 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
57 }
58 if (given)
59 this->add(given);
60 mReqs = make();
61 }
62
63
64 //
65 // BlobWriters
66 //
67 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
68 {
69 return EmbeddedSignatureBlob::Maker::component(slot, data);
70 }
71
72
73 void DetachedBlobWriter::flush()
74 {
75 EmbeddedSignatureBlob *blob = this->make();
76 signer.code->detachedSignature(CFTempData(*blob));
77 signer.state.returnDetachedSignature(blob);
78 ::free(blob);
79 }
80
81
82 //
83 // ArchEditor
84 //
85 ArchEditor::ArchEditor(Universal &code, uint32_t attrs /* = 0 */)
86 : DiskRep::Writer(attrs)
87 {
88 Universal::Architectures archList;
89 code.architectures(archList);
90 for (Universal::Architectures::const_iterator it = archList.begin();
91 it != archList.end(); ++it)
92 architecture[*it] = new Arch(*it);
93 }
94
95
96 ArchEditor::~ArchEditor()
97 {
98 for (ArchMap::iterator it = begin(); it != end(); ++it)
99 delete it->second;
100 }
101
102
103 //
104 // BlobEditor
105 //
106 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
107 {
108 mGlobal.component(slot, data);
109 }
110
111 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
112 {
113 mMaker.add(arch.architecture.cpuType(), blob);
114 }
115
116
117 void BlobEditor::commit()
118 {
119 // create the architecture-global blob and store it into the superblob
120 mMaker.add(0, mGlobal.make()); // takes ownership of blob
121
122 // finish up the superblob and deliver it
123 DetachedSignatureBlob *blob = mMaker.make();
124 signer.state.returnDetachedSignature(blob);
125 ::free(blob);
126 }
127
128
129 //
130 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
131 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
132 // After the tool succeeds, we open the new file and are ready to write it.
133 //
134 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath)
135 : ArchEditor(code, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"),
136 mNewCode(NULL), mTempMayExist(false)
137 {
138 }
139
140 MachOEditor::~MachOEditor()
141 {
142 delete mNewCode;
143 if (mTempMayExist)
144 ::remove(tempPath.c_str()); // ignore error (can't do anything about it)
145 }
146
147
148 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
149 {
150 writer->component(slot, data);
151 }
152
153
154 void MachOEditor::allocate()
155 {
156 // note that we may have a temporary file from now on (for cleanup in the error case)
157 mTempMayExist = true;
158
159 // run codesign_allocate to make room in the executable file
160 fork();
161 wait();
162 if (!Child::succeeded())
163 UnixError::throwMe(ENOEXEC); //@@@ how to signal "it din' work"?
164
165 // open the new (temporary) Universal file
166 {
167 UidGuard guard(0);
168 mFd.open(tempPath, O_RDWR);
169 }
170 mNewCode = new Universal(mFd);
171 }
172
173 void MachOEditor::childAction()
174 {
175 vector<const char *> arguments;
176 arguments.push_back(helperName);
177 arguments.push_back("-i");
178 arguments.push_back(sourcePath.c_str());
179 arguments.push_back("-o");
180 arguments.push_back(tempPath.c_str());
181
182 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
183 char *size; // we'll leak this (execv is coming soon)
184 asprintf(&size, "%d", LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign));
185 secdebug("machoedit", "preparing %s size=%s", it->first.name(), size);
186 arguments.push_back("-a");
187 arguments.push_back(it->first.name());
188 arguments.push_back(size);
189 }
190 arguments.push_back(NULL);
191 ::seteuid(0); // activate privilege if caller has it; ignore error if not
192 execv(helperPath, (char * const *)&arguments[0]);
193 }
194
195 void MachOEditor::reset(Arch &arch)
196 {
197 arch.source.reset(mNewCode->architecture(arch.architecture));
198 arch.cdbuilder.reopen(tempPath,
199 arch.source->offset(), arch.source->signingOffset());
200 }
201
202
203 //
204 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
205 // of the executable image file.
206 //
207 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
208 {
209 if (size_t offset = arch.source->signingOffset()) {
210 size_t signingLength = arch.source->signingLength();
211 secdebug("codesign", "writing architecture %s at 0x%zx (%zd of %zd)",
212 arch.architecture.name(), offset, blob->length(), signingLength);
213 if (signingLength < blob->length()) {
214 secdebug("codesign", "trying to write %zd bytes into %zd area",
215 blob->length(), signingLength);
216 MacOSError::throwMe(errSecCSInternalError);
217 }
218 arch.source->seek(offset);
219 arch.source->writeAll(*blob);
220 ::free(blob); // done with it
221 } else {
222 secdebug("signer", "%p cannot find CODESIGNING section", this);
223 MacOSError::throwMe(errSecCSInternalError);
224 }
225 }
226
227
228 //
229 // Commit the edit.
230 // This moves the temporary editor copy over the source image file.
231 // Note that the Universal object returned by allocate() is still open
232 // and valid; the caller owns it.
233 //
234 void MachOEditor::commit()
235 {
236 // if the file's owned by someone else *and* we can become root...
237 struct stat st;
238 UnixError::check(::stat(sourcePath.c_str(), &st));
239
240 // copy over all the *other* stuff
241 Copyfile copy;
242 int fd = mFd;
243 copy.set(COPYFILE_STATE_DST_FD, &fd);
244 {
245 // perform copy under root or file-owner privileges if available
246 UidGuard guard;
247 if (!guard.seteuid(0))
248 guard.seteuid(st.st_uid);
249 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
250
251 // move the new file into place
252 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
253 mTempMayExist = false; // we renamed it away
254 }
255 }
256
257
258 //
259 // Copyfile
260 //
261 Copyfile::Copyfile()
262 {
263 if (!(mState = copyfile_state_alloc()))
264 UnixError::throwMe();
265 }
266
267 void Copyfile::set(uint32_t flag, const void *value)
268 {
269 check(::copyfile_state_set(mState, flag, value));
270 }
271
272 void Copyfile::get(uint32_t flag, void *value)
273 {
274 check(::copyfile_state_set(mState, flag, value));
275 }
276
277 void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags)
278 {
279 check(::copyfile(src, dst, mState, flags));
280 }
281
282 void Copyfile::check(int rc)
283 {
284 if (rc < 0)
285 UnixError::throwMe();
286 }
287
288
289 } // end namespace CodeSigning
290 } // end namespace Security