]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signerutils.cpp
a7e925ae216ae66b5a7c9842b61bc6f19404fba0
[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 "csutilities.h"
34 #include <security_utilities/unix++.h>
35 #include <security_utilities/unixchild.h>
36 #include <vector>
37
38 // for helper validation
39 #include "Code.h"
40 #include <security_utilities/cfmunge.h>
41 #include <sys/codesign.h>
42
43
44 namespace Security {
45 namespace CodeSigning {
46
47
48 //
49 // About the Mach-O allocation helper
50 //
51 static const char helperName[] = "codesign_allocate";
52 static const char helperPath[] = "/usr/bin/codesign_allocate";
53 static const char helperOverride[] = "CODESIGN_ALLOCATE";
54 static const size_t csAlign = 16;
55
56
57 //
58 // BlobWriters
59 //
60 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
61 {
62 return EmbeddedSignatureBlob::Maker::component(slot, data);
63 }
64
65
66 void DetachedBlobWriter::flush()
67 {
68 EmbeddedSignatureBlob *blob = this->make();
69 signer.code->detachedSignature(makeCFData(*blob));
70 signer.state.returnDetachedSignature(blob, signer);
71 ::free(blob);
72 }
73
74
75 //
76 // ArchEditor
77 //
78 ArchEditor::ArchEditor(Universal &code, uint32_t attrs /* = 0 */)
79 : DiskRep::Writer(attrs)
80 {
81 Universal::Architectures archList;
82 code.architectures(archList);
83 for (Universal::Architectures::const_iterator it = archList.begin();
84 it != archList.end(); ++it)
85 architecture[*it] = new Arch(*it);
86 }
87
88
89 ArchEditor::~ArchEditor()
90 {
91 for (ArchMap::iterator it = begin(); it != end(); ++it)
92 delete it->second;
93 }
94
95
96 //
97 // BlobEditor
98 //
99 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
100 {
101 mGlobal.component(slot, data);
102 }
103
104 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
105 {
106 mMaker.add(arch.architecture.cpuType(), blob);
107 }
108
109
110 void BlobEditor::commit()
111 {
112 // create the architecture-global blob and store it into the superblob
113 mMaker.add(0, mGlobal.make()); // takes ownership of blob
114
115 // finish up the superblob and deliver it
116 DetachedSignatureBlob *blob = mMaker.make();
117 signer.state.returnDetachedSignature(blob, signer);
118 ::free(blob);
119 }
120
121
122 //
123 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
124 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
125 // After the tool succeeds, we open the new file and are ready to write it.
126 //
127 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath)
128 : ArchEditor(code, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"),
129 mNewCode(NULL), mTempMayExist(false)
130 {
131 if (const char *path = getenv(helperOverride)) {
132 mHelperPath = path;
133 mHelperOverridden = true;
134 } else {
135 mHelperPath = helperPath;
136 mHelperOverridden = 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 this->kill();
146 }
147
148
149 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
150 {
151 writer->component(slot, data);
152 }
153
154
155 void MachOEditor::allocate()
156 {
157 // note that we may have a temporary file from now on (for cleanup in the error case)
158 mTempMayExist = true;
159
160 // run codesign_allocate to make room in the executable file
161 fork();
162 wait();
163 if (!Child::succeeded())
164 UnixError::throwMe(ENOEXEC); //@@@ how to signal "it din' work"?
165
166 // open the new (temporary) Universal file
167 {
168 UidGuard guard(0);
169 mFd.open(tempPath, O_RDWR);
170 }
171 mNewCode = new Universal(mFd);
172 }
173
174 static const unsigned char appleReq[] = {
175 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
176 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
177 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
178 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
180 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
181 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
182 };
183
184 void MachOEditor::parentAction()
185 {
186 if (mHelperOverridden) {
187 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
188 // check code identity of an overridden allocation helper
189 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
190 code->validateDirectory();
191 code->validateExecutable();
192 code->validateResources();
193 code->validateRequirements((const Requirement *)appleReq, errSecCSReqFailed);
194 }
195 }
196
197 void MachOEditor::childAction()
198 {
199 vector<const char *> arguments;
200 arguments.push_back(helperName);
201 arguments.push_back("-i");
202 arguments.push_back(sourcePath.c_str());
203 arguments.push_back("-o");
204 arguments.push_back(tempPath.c_str());
205
206 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
207 size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
208 char *ssize; // we'll leak this (execv is coming soon)
209 asprintf(&ssize, "%d", size);
210
211 if (const char *arch = it->first.name()) {
212 CODESIGN_ALLOCATE_ARCH((char*)arch, size);
213 arguments.push_back("-a");
214 arguments.push_back(arch);
215 } else {
216 CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), size);
217 arguments.push_back("-A");
218 char *anum;
219 asprintf(&anum, "%d", it->first.cpuType());
220 arguments.push_back(anum);
221 asprintf(&anum, "%d", it->first.cpuSubtype());
222 arguments.push_back(anum);
223 }
224 arguments.push_back(ssize);
225 }
226 arguments.push_back(NULL);
227
228 if (mHelperOverridden)
229 ::csops(0, CS_EXEC_SET_KILL, NULL, 0); // force code integrity
230 ::seteuid(0); // activate privilege if caller has it; ignore error if not
231 execv(mHelperPath, (char * const *)&arguments[0]);
232 }
233
234 void MachOEditor::reset(Arch &arch)
235 {
236 arch.source.reset(mNewCode->architecture(arch.architecture));
237 arch.cdbuilder.reopen(tempPath,
238 arch.source->offset(), arch.source->signingOffset());
239 }
240
241
242 //
243 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
244 // of the executable image file.
245 //
246 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
247 {
248 if (size_t offset = arch.source->signingOffset()) {
249 size_t signingLength = arch.source->signingLength();
250 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, blob->length(), signingLength);
251 if (signingLength < blob->length())
252 MacOSError::throwMe(errSecCSCMSTooLarge);
253 arch.source->seek(offset);
254 arch.source->writeAll(*blob);
255 ::free(blob); // done with it
256 } else {
257 secdebug("signer", "%p cannot find CODESIGNING section", this);
258 MacOSError::throwMe(errSecCSInternalError);
259 }
260 }
261
262
263 //
264 // Commit the edit.
265 // This moves the temporary editor copy over the source image file.
266 // Note that the Universal object returned by allocate() is still open
267 // and valid; the caller owns it.
268 //
269 void MachOEditor::commit()
270 {
271 // if the file's owned by someone else *and* we can become root...
272 struct stat st;
273 UnixError::check(::stat(sourcePath.c_str(), &st));
274
275 // copy over all the *other* stuff
276 Copyfile copy;
277 int fd = mFd;
278 copy.set(COPYFILE_STATE_DST_FD, &fd);
279 {
280 // perform copy under root or file-owner privileges if available
281 UidGuard guard;
282 if (!guard.seteuid(0))
283 guard.seteuid(st.st_uid);
284 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
285
286 // move the new file into place
287 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
288 mTempMayExist = false; // we renamed it away
289 }
290 }
291
292
293 } // end namespace CodeSigning
294 } // end namespace Security