]> git.saurik.com Git - apple/security.git/blob - libsecurity_codesigning/lib/signerutils.cpp
Security-55471.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / signerutils.cpp
1 /*
2 * Copyright (c) 2006-2010 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 "resources.h"
33 #include "csutilities.h"
34 #include "drmaker.h"
35 #include <security_utilities/unix++.h>
36 #include <security_utilities/unixchild.h>
37 #include <vector>
38
39 // for helper validation
40 #include "Code.h"
41 #include <security_utilities/cfmunge.h>
42 #include <sys/codesign.h>
43
44
45 namespace Security {
46 namespace CodeSigning {
47
48
49 //
50 // About the Mach-O allocation helper
51 //
52 static const char helperName[] = "codesign_allocate";
53 static const char helperPath[] = "/usr/bin/codesign_allocate";
54 static const char helperOverride[] = "CODESIGN_ALLOCATE";
55 static const size_t csAlign = 16;
56
57
58 //
59 // BlobWriters
60 //
61 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
62 {
63 return EmbeddedSignatureBlob::Maker::component(slot, data);
64 }
65
66
67 void DetachedBlobWriter::flush()
68 {
69 EmbeddedSignatureBlob *blob = this->make();
70 signer.code->detachedSignature(CFTempData(*blob));
71 signer.state.returnDetachedSignature(blob, signer);
72 ::free(blob);
73 }
74
75
76 //
77 // ArchEditor
78 //
79 ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, uint32_t attrs)
80 : DiskRep::Writer(attrs)
81 {
82 Universal::Architectures archList;
83 code.architectures(archList);
84 for (Universal::Architectures::const_iterator it = archList.begin();
85 it != archList.end(); ++it)
86 architecture[*it] = new Arch(*it, hashType);
87 }
88
89
90 ArchEditor::~ArchEditor()
91 {
92 for (ArchMap::iterator it = begin(); it != end(); ++it)
93 delete it->second;
94 }
95
96
97 //
98 // BlobEditor
99 //
100 BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
101 : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s)
102 { }
103
104
105 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
106 {
107 mGlobal.component(slot, data);
108 }
109
110 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
111 {
112 mMaker.add(arch.architecture.cpuType(), blob);
113 }
114
115
116 void BlobEditor::commit()
117 {
118 // create the architecture-global blob and store it into the superblob
119 mMaker.add(0, mGlobal.make()); // takes ownership of blob
120
121 // finish up the superblob and deliver it
122 DetachedSignatureBlob *blob = mMaker.make();
123 signer.state.returnDetachedSignature(blob, signer);
124 ::free(blob);
125 }
126
127
128 //
129 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
130 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
131 // After the tool succeeds, we open the new file and are ready to write it.
132 //
133 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithm hashType, std::string srcPath)
134 : ArchEditor(code, hashType, w->attributes()),
135 writer(w),
136 sourcePath(srcPath),
137 tempPath(srcPath + ".cstemp"),
138 mNewCode(NULL),
139 mTempMayExist(false)
140 {
141 if (const char *path = getenv(helperOverride)) {
142 mHelperPath = path;
143 mHelperOverridden = true;
144 } else {
145 mHelperPath = helperPath;
146 mHelperOverridden = false;
147 }
148 }
149
150 MachOEditor::~MachOEditor()
151 {
152 delete mNewCode;
153 if (mTempMayExist)
154 ::remove(tempPath.c_str()); // ignore error (can't do anything about it)
155 this->kill();
156 }
157
158
159 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
160 {
161 writer->component(slot, data);
162 }
163
164
165 void MachOEditor::allocate()
166 {
167 // note that we may have a temporary file from now on (for cleanup in the error case)
168 mTempMayExist = true;
169
170 // run codesign_allocate to make room in the executable file
171 fork();
172 wait();
173 if (!Child::succeeded())
174 MacOSError::throwMe(errSecCSHelperFailed);
175
176 // open the new (temporary) Universal file
177 {
178 UidGuard guard(0);
179 mFd.open(tempPath, O_RDWR);
180 }
181 mNewCode = new Universal(mFd);
182 }
183
184 static const unsigned char appleReq[] = {
185 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
186 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
187 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
188 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
190 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
191 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
192 };
193
194 void MachOEditor::parentAction()
195 {
196 if (mHelperOverridden) {
197 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
198 // check code identity of an overridden allocation helper
199 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
200 code->staticValidate(kSecCSDefaultFlags, NULL);
201 code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed);
202 }
203 }
204
205 void MachOEditor::childAction()
206 {
207 vector<const char *> arguments;
208 arguments.push_back(helperName);
209 arguments.push_back("-i");
210 arguments.push_back(sourcePath.c_str());
211 arguments.push_back("-o");
212 arguments.push_back(tempPath.c_str());
213
214 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
215 size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
216 char *ssize; // we'll leak this (execv is coming soon)
217 asprintf(&ssize, "%zd", size);
218
219 if (const char *arch = it->first.name()) {
220 CODESIGN_ALLOCATE_ARCH((char*)arch, (unsigned int)size);
221 arguments.push_back("-a");
222 arguments.push_back(arch);
223 } else {
224 CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size);
225 arguments.push_back("-A");
226 char *anum;
227 asprintf(&anum, "%d", it->first.cpuType());
228 arguments.push_back(anum);
229 asprintf(&anum, "%d", it->first.cpuSubtype());
230 arguments.push_back(anum);
231 }
232 arguments.push_back(ssize);
233 }
234 arguments.push_back(NULL);
235
236 if (mHelperOverridden)
237 ::csops(0, CS_EXEC_SET_KILL, NULL, 0); // force code integrity
238 ::seteuid(0); // activate privilege if caller has it; ignore error if not
239 execv(mHelperPath, (char * const *)&arguments[0]);
240 }
241
242 void MachOEditor::reset(Arch &arch)
243 {
244 arch.source.reset(mNewCode->architecture(arch.architecture));
245 arch.cdbuilder.reopen(tempPath,
246 arch.source->offset(), arch.source->signingOffset());
247 }
248
249
250 //
251 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
252 // of the executable image file.
253 //
254 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
255 {
256 if (size_t offset = arch.source->signingOffset()) {
257 size_t signingLength = arch.source->signingLength();
258 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength);
259 if (signingLength < blob->length())
260 MacOSError::throwMe(errSecCSCMSTooLarge);
261 arch.source->seek(offset);
262 arch.source->writeAll(*blob);
263 ::free(blob); // done with it
264 } else {
265 secdebug("signer", "%p cannot find CODESIGNING section", this);
266 MacOSError::throwMe(errSecCSInternalError);
267 }
268 }
269
270
271 //
272 // Commit the edit.
273 // This moves the temporary editor copy over the source image file.
274 // Note that the Universal object returned by allocate() is still open
275 // and valid; the caller owns it.
276 //
277 void MachOEditor::commit()
278 {
279 // if the file's owned by someone else *and* we can become root...
280 struct stat st;
281 UnixError::check(::stat(sourcePath.c_str(), &st));
282
283 // copy over all the *other* stuff
284 Copyfile copy;
285 int fd = mFd;
286 copy.set(COPYFILE_STATE_DST_FD, &fd);
287 {
288 // perform copy under root or file-owner privileges if available
289 UidGuard guard;
290 if (!guard.seteuid(0))
291 guard.seteuid(st.st_uid);
292
293 // copy metadata from original file...
294 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
295
296 // ... but explicitly update the timestamps since we did change the file
297 char buf;
298 mFd.read(&buf, sizeof(buf), 0);
299 mFd.write(&buf, sizeof(buf), 0);
300
301 // move the new file into place
302 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
303 mTempMayExist = false; // we renamed it away
304 }
305 }
306
307
308 //
309 // InternalRequirements
310 //
311 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
312 {
313 // first add the default internal requirements
314 if (defaulted) {
315 this->add(defaulted);
316 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
317 }
318
319 // now override them with any requirements explicitly given by the signer
320 if (given)
321 this->add(given);
322
323 // now add the Designated Requirement, if we can make it and it's not been provided
324 if (!this->contains(kSecDesignatedRequirementType)) {
325 DRMaker maker(context);
326 if (Requirement *dr = maker.make()) {
327 this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr
328 }
329 }
330
331 // return the result
332 mReqs = this->make();
333 }
334
335
336 //
337 // Pre-Signing contexts
338 //
339 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
340 {
341 // construct a cert chain
342 if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
343 CFRef<SecCertificateRef> signingCert;
344 MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
345 CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
346 CFRef<SecTrustRef> trust;
347 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
348 SecTrustResultType result;
349 MacOSError::check(SecTrustEvaluate(trust, &result));
350 CSSM_TP_APPLE_EVIDENCE_INFO *info;
351 MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
352 this->certs = mCerts;
353 }
354
355 // other stuff
356 this->identifier = signer.signingIdentifier();
357 }
358
359
360 } // end namespace CodeSigning
361 } // end namespace Security