]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signerutils.cpp
Security-58286.230.21.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 "csutilities.h"
28 #include "drmaker.h"
29 #include "resources.h"
30 #include "signerutils.h"
31 #include "signer.h"
32
33 #include <Security/SecCmsBase.h>
34 #include <Security/SecIdentity.h>
35 #include <Security/CMSEncoder.h>
36
37 #include "SecCodeSigner.h"
38
39 #include <security_utilities/unix++.h>
40 #include <security_utilities/logging.h>
41 #include <security_utilities/unixchild.h>
42
43 #include <vector>
44
45 // for helper validation
46 #include "Code.h"
47 #include <security_utilities/cfmunge.h>
48 #include <sys/codesign.h>
49
50
51 namespace Security {
52 namespace CodeSigning {
53
54
55 //
56 // About the Mach-O allocation helper
57 //
58 static const char helperName[] = "codesign_allocate";
59 static const char helperPath[] = "/usr/bin/codesign_allocate";
60 static const char helperOverride[] = "CODESIGN_ALLOCATE";
61 static const size_t csAlign = 16;
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, signer);
78 ::free(blob);
79 }
80
81
82 //
83 // ArchEditor
84 //
85 ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithms hashTypes, uint32_t attrs)
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, hashTypes);
93 }
94
95
96 ArchEditor::~ArchEditor()
97 {
98 for (ArchMap::iterator it = begin(); it != end(); ++it)
99 delete it->second;
100 }
101
102
103 ArchEditor::Arch::Arch(const Architecture &arch, CodeDirectory::HashAlgorithms hashTypes)
104 : architecture(arch)
105 {
106 blobSize = 0;
107 for (auto type = hashTypes.begin(); type != hashTypes.end(); ++type)
108 cdBuilders.insert(make_pair(*type, new CodeDirectory::Builder(*type)));
109 }
110
111
112 //
113 // BlobEditor
114 //
115 BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
116 : ArchEditor(fat, s.digestAlgorithms(), 0), signer(s)
117 { }
118
119
120 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
121 {
122 mGlobal.component(slot, data);
123 }
124
125 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
126 {
127 mMaker.add(arch.architecture.cpuType(), blob);
128 }
129
130
131 void BlobEditor::commit()
132 {
133 // create the architecture-global blob and store it into the superblob
134 mMaker.add(0, mGlobal.make()); // takes ownership of blob
135
136 // finish up the superblob and deliver it
137 DetachedSignatureBlob *blob = mMaker.make();
138 signer.state.returnDetachedSignature(blob, signer);
139 ::free(blob);
140 }
141
142
143 //
144 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to
145 // "drill up" the Mach-O binary for insertion of Code Signing signature data.
146 // After the tool succeeds, we open the new file and are ready to write it.
147 //
148 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithms hashTypes, std::string srcPath)
149 : ArchEditor(code, hashTypes, w->attributes()),
150 writer(w),
151 sourcePath(srcPath),
152 tempPath(srcPath + ".cstemp"),
153 mHashTypes(hashTypes),
154 mNewCode(NULL),
155 mTempMayExist(false)
156 {
157 if (const char *path = getenv(helperOverride)) {
158 mHelperPath = path;
159 mHelperOverridden = true;
160 } else {
161 mHelperPath = helperPath;
162 mHelperOverridden = false;
163 }
164 }
165
166 MachOEditor::~MachOEditor()
167 {
168 delete mNewCode;
169 if (mTempMayExist)
170 ::remove(tempPath.c_str()); // ignore error (can't do anything about it)
171
172 this->kill();
173 }
174
175
176 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
177 {
178 writer->component(slot, data);
179 }
180
181
182 void MachOEditor::allocate()
183 {
184 // note that we may have a temporary file from now on (for cleanup in the error case)
185 mTempMayExist = true;
186
187 // run codesign_allocate to make room in the executable file
188 fork();
189 wait();
190 if (!Child::succeeded())
191 MacOSError::throwMe(errSecCSHelperFailed);
192
193 // open the new (temporary) Universal file
194 {
195 UidGuard guard(0);
196 mFd.open(tempPath, O_RDWR);
197 }
198 mNewCode = new Universal(mFd);
199 }
200
201 static const unsigned char appleReq[] = {
202 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
203 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
204 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
205 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
206 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
207 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
208 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
209 };
210
211 void MachOEditor::parentAction()
212 {
213 if (mHelperOverridden) {
214 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
215 // check code identity of an overridden allocation helper
216 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
217 code->staticValidate(kSecCSDefaultFlags, NULL);
218 code->validateRequirement((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 size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
233 char *ssize; // we'll leak this (execv is coming soon)
234 asprintf(&ssize, "%zd", size);
235
236 if (const char *arch = it->first.name()) {
237 CODESIGN_ALLOCATE_ARCH((char*)arch, (unsigned int)size);
238 arguments.push_back("-a");
239 arguments.push_back(arch);
240 } else {
241 CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size);
242 arguments.push_back("-A");
243 char *anum;
244 asprintf(&anum, "%d", it->first.cpuType());
245 arguments.push_back(anum);
246 asprintf(&anum, "%d", it->first.cpuSubtype());
247 arguments.push_back(anum);
248 }
249 arguments.push_back(ssize);
250 }
251 arguments.push_back(NULL);
252
253 if (mHelperOverridden)
254 ::csops(0, CS_OPS_MARKKILL, NULL, 0); // force code integrity
255 (void)::seteuid(0); // activate privilege if caller has it; ignore error if not
256 execv(mHelperPath, (char * const *)&arguments[0]);
257 }
258
259 void MachOEditor::reset(Arch &arch)
260 {
261 arch.source.reset(mNewCode->architecture(arch.architecture));
262
263 for (auto type = mHashTypes.begin(); type != mHashTypes.end(); ++type) {
264 arch.eachDigest(^(CodeDirectory::Builder& builder) {
265 builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset());
266 });
267 }
268 }
269
270
271 //
272 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
273 // of the executable image file.
274 //
275 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
276 {
277 if (size_t offset = arch.source->signingOffset()) {
278 size_t signingLength = arch.source->signingLength();
279 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength);
280 if (signingLength < blob->length())
281 MacOSError::throwMe(errSecCSCMSTooLarge);
282 arch.source->seek(offset);
283 arch.source->writeAll(*blob);
284 ::free(blob); // done with it
285 } else {
286 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
287 MacOSError::throwMe(errSecCSInternalError);
288 }
289 }
290
291
292 //
293 // Commit the edit.
294 // This moves the temporary editor copy over the source image file.
295 // Note that the Universal object returned by allocate() is still open
296 // and valid; the caller owns it.
297 //
298 void MachOEditor::commit()
299 {
300 // if the file's owned by someone else *and* we can become root...
301 struct stat st;
302 UnixError::check(::stat(sourcePath.c_str(), &st));
303
304 // copy over all the *other* stuff
305 Copyfile copy;
306 int fd = mFd;
307 copy.set(COPYFILE_STATE_DST_FD, &fd);
308 {
309 // perform copy under root or file-owner privileges if available
310 UidGuard guard;
311 if (!guard.seteuid(0))
312 (void)guard.seteuid(st.st_uid);
313
314 // copy metadata from original file...
315 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
316
317 #if TARGET_OS_OSX
318 // determine AFSC status if we are told to preserve compression
319 bool conductCompression = false;
320 cmpInfo cInfo;
321 if (writer->getPreserveAFSC()) {
322 if (queryCompressionInfo(sourcePath.c_str(), &cInfo) == 0) {
323 if (cInfo.compressionType != 0 && cInfo.compressedSize > 0)
324 conductCompression = true;
325 }
326 }
327 #endif
328
329 // ... but explicitly update the timestamps since we did change the file
330 char buf;
331 mFd.read(&buf, sizeof(buf), 0);
332 mFd.write(&buf, sizeof(buf), 0);
333
334 // move the new file into place
335 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
336 mTempMayExist = false; // we renamed it away
337
338 #if TARGET_OS_OSX
339 // if the original file was compressed, compress the new file after move
340 if (conductCompression) {
341 CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
342 CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType);
343 CFDictionarySetValue(options, kAFSCCompressionTypes, val);
344 CFRelease(val);
345
346 CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options);
347
348 if (!CompressFile(compressionQueue, sourcePath.c_str(), NULL)) {
349 secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath.c_str());
350 MacOSError::throwMe(errSecCSInternalError);
351 }
352 FinishCompressionAndCleanUp(compressionQueue);
353
354 compressionQueue = NULL;
355 CFRelease(options);
356 }
357 #endif
358
359 }
360 this->writer->flush();
361 }
362
363
364 //
365 // InternalRequirements
366 //
367 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
368 {
369 // first add the default internal requirements
370 if (defaulted) {
371 this->add(defaulted);
372 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
373 }
374
375 // now override them with any requirements explicitly given by the signer
376 if (given)
377 this->add(given);
378
379 // now add the Designated Requirement, if we can make it and it's not been provided
380 if (!this->contains(kSecDesignatedRequirementType)) {
381 DRMaker maker(context);
382 if (Requirement *dr = maker.make()) {
383 this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr
384 }
385 }
386
387 // return the result
388 mReqs = this->make();
389 }
390
391
392 //
393 // Pre-Signing contexts
394 //
395 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
396 {
397 // construct a cert chain
398 if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
399 CFRef<SecCertificateRef> signingCert;
400 MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
401 CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
402 CFRef<SecTrustRef> trust;
403 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
404 SecTrustResultType result;
405 MacOSError::check(SecTrustEvaluate(trust, &result));
406 CSSM_TP_APPLE_EVIDENCE_INFO *info;
407 MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
408 this->certs = mCerts;
409 }
410
411 // other stuff
412 this->identifier = signer.signingIdentifier();
413 }
414
415
416 //
417 // A collector of CodeDirectories for hash-agile construction of signatures.
418 //
419 CodeDirectorySet::~CodeDirectorySet()
420 {
421 for (auto it = begin(); it != end(); ++it)
422 ::free(const_cast<CodeDirectory*>(it->second));
423 }
424
425
426 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd)
427 {
428 insert(make_pair(cd->hashType, cd));
429 if (cd->hashType == kSecCodeSignatureHashSHA1)
430 mPrimary = cd;
431 }
432
433
434 void CodeDirectorySet::populate(DiskRep::Writer *writer) const
435 {
436 assert(!empty());
437
438 if (mPrimary == NULL) // didn't add SHA-1; pick another occupant for this slot
439 mPrimary = begin()->second;
440
441 // reserve slot zero for a SHA-1 digest if present; else pick something else
442 CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots;
443 for (auto it = begin(); it != end(); ++it) {
444 if (it->second == mPrimary) {
445 writer->codeDirectory(it->second, cdCodeDirectorySlot);
446 } else {
447 writer->codeDirectory(it->second, nextAlternate++);
448 }
449 }
450 }
451
452
453 const CodeDirectory* CodeDirectorySet::primary() const
454 {
455 if (mPrimary == NULL)
456 mPrimary = begin()->second;
457 return mPrimary;
458 }
459
460 CFArrayRef CodeDirectorySet::hashList() const
461 {
462 CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0);
463 for (auto it = begin(); it != end(); ++it) {
464 CFRef<CFDataRef> cdhash = it->second->cdhash(true);
465 CFArrayAppendValue(hashList, cdhash);
466 }
467 return hashList.yield();
468 }
469
470 CFDictionaryRef CodeDirectorySet::hashDict() const
471 {
472 CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary();
473
474 for (auto it = begin(); it != end(); ++it) {
475 SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first);
476
477 if (tag == SEC_OID_UNKNOWN) {
478 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
479 }
480
481 CFRef<CFNumberRef> hashType = makeCFNumber(int(tag));
482 CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash!
483 CFDictionarySetValue(hashDict, hashType, fullCdhash);
484 }
485
486 return hashDict.yield();
487 }
488
489 SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) {
490 SECOidTag tag;
491
492 switch (algorithm) {
493 case kSecCodeSignatureHashSHA1:
494 tag = SEC_OID_SHA1;
495 break;
496 case kSecCodeSignatureHashSHA256:
497 case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash
498 tag = SEC_OID_SHA256;
499 break;
500 case kSecCodeSignatureHashSHA384:
501 tag = SEC_OID_SHA384;
502 break;
503 default:
504 tag = SEC_OID_UNKNOWN;
505 }
506
507 return tag;
508 }
509
510
511
512 } // end namespace CodeSigning
513 } // end namespace Security