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