]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signerutils.cpp
Security-59754.80.3.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 /* Signature editing may have no need for cd builders, and not
266 * have opened them, so only reopen them conditionally. */
267 if (builder.opened()) {
268 builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset());
269 }
270 });
271 }
272 }
273
274
275 //
276 // MachOEditor's write() method actually writes the blob into the CODESIGNING section
277 // of the executable image file.
278 //
279 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
280 {
281 if (size_t offset = arch.source->signingOffset()) {
282 size_t signingLength = arch.source->signingLength();
283 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength);
284 if (signingLength < blob->length())
285 MacOSError::throwMe(errSecCSCMSTooLarge);
286 arch.source->seek(offset);
287 arch.source->writeAll(*blob);
288 ::free(blob); // done with it
289 } else {
290 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this);
291 MacOSError::throwMe(errSecCSInternalError);
292 }
293 }
294
295
296 //
297 // Commit the edit.
298 // This moves the temporary editor copy over the source image file.
299 // Note that the Universal object returned by allocate() is still open
300 // and valid; the caller owns it.
301 //
302 void MachOEditor::commit()
303 {
304 // if the file's owned by someone else *and* we can become root...
305 struct stat st;
306 UnixError::check(::stat(sourcePath.c_str(), &st));
307
308 // copy over all the *other* stuff
309 Copyfile copy;
310 int fd = mFd;
311 copy.set(COPYFILE_STATE_DST_FD, &fd);
312 {
313 // perform copy under root or file-owner privileges if available
314 UidGuard guard;
315 if (!guard.seteuid(0))
316 (void)guard.seteuid(st.st_uid);
317
318 // copy metadata from original file...
319 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
320
321 #if TARGET_OS_OSX
322 // determine AFSC status if we are told to preserve compression
323 bool conductCompression = false;
324 cmpInfo cInfo;
325 if (writer->getPreserveAFSC()) {
326 if (queryCompressionInfo(sourcePath.c_str(), &cInfo) == 0) {
327 if (cInfo.compressionType != 0 && cInfo.compressedSize > 0)
328 conductCompression = true;
329 }
330 }
331 #endif
332
333 // ... but explicitly update the timestamps since we did change the file
334 char buf;
335 mFd.read(&buf, sizeof(buf), 0);
336 mFd.write(&buf, sizeof(buf), 0);
337
338 // move the new file into place
339 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
340 mTempMayExist = false; // we renamed it away
341
342 #if TARGET_OS_OSX
343 // if the original file was compressed, compress the new file after move
344 if (conductCompression) {
345 CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
346 CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType);
347 CFDictionarySetValue(options, kAFSCCompressionTypes, val);
348 CFRelease(val);
349
350 CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options);
351
352 if (!CompressFile(compressionQueue, sourcePath.c_str(), NULL)) {
353 secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath.c_str());
354 MacOSError::throwMe(errSecCSInternalError);
355 }
356 FinishCompressionAndCleanUp(compressionQueue);
357
358 compressionQueue = NULL;
359 CFRelease(options);
360 }
361 #endif
362
363 }
364 this->writer->flush();
365 }
366
367
368 //
369 // InternalRequirements
370 //
371 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
372 {
373 // first add the default internal requirements
374 if (defaulted) {
375 this->add(defaulted);
376 ::free((void *)defaulted); // was malloc(3)ed by DiskRep
377 }
378
379 // now override them with any requirements explicitly given by the signer
380 if (given)
381 this->add(given);
382
383 // now add the Designated Requirement, if we can make it and it's not been provided
384 if (!this->contains(kSecDesignatedRequirementType)) {
385 DRMaker maker(context);
386 if (Requirement *dr = maker.make()) {
387 this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr
388 }
389 }
390
391 // return the result
392 mReqs = this->make();
393 }
394
395
396 //
397 // Pre-Signing contexts
398 //
399 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
400 {
401 // construct a cert chain
402 if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
403 CFRef<SecCertificateRef> signingCert;
404 MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
405 CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
406 CFRef<SecTrustRef> trust;
407 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
408 SecTrustResultType result;
409 MacOSError::check(SecTrustEvaluate(trust, &result));
410 CSSM_TP_APPLE_EVIDENCE_INFO *info;
411 MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
412 this->certs = mCerts;
413 }
414
415 // other stuff
416 this->identifier = signer.signingIdentifier();
417 }
418
419
420 //
421 // A collector of CodeDirectories for hash-agile construction of signatures.
422 //
423 CodeDirectorySet::~CodeDirectorySet()
424 {
425 for (auto it = begin(); it != end(); ++it)
426 ::free(const_cast<CodeDirectory*>(it->second));
427 }
428
429
430 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd)
431 {
432 insert(make_pair(cd->hashType, cd));
433 if (cd->hashType == kSecCodeSignatureHashSHA1)
434 mPrimary = cd;
435 }
436
437
438 void CodeDirectorySet::populate(DiskRep::Writer *writer) const
439 {
440 assert(!empty());
441
442 if (mPrimary == NULL) // didn't add SHA-1; pick another occupant for this slot
443 mPrimary = begin()->second;
444
445 // reserve slot zero for a SHA-1 digest if present; else pick something else
446 CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots;
447 for (auto it = begin(); it != end(); ++it) {
448 if (it->second == mPrimary) {
449 writer->codeDirectory(it->second, cdCodeDirectorySlot);
450 } else {
451 writer->codeDirectory(it->second, nextAlternate++);
452 }
453 }
454 }
455
456
457 const CodeDirectory* CodeDirectorySet::primary() const
458 {
459 if (mPrimary == NULL)
460 mPrimary = begin()->second;
461 return mPrimary;
462 }
463
464 CFArrayRef CodeDirectorySet::hashList() const
465 {
466 CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0);
467 for (auto it = begin(); it != end(); ++it) {
468 CFRef<CFDataRef> cdhash = it->second->cdhash(true);
469 CFArrayAppendValue(hashList, cdhash);
470 }
471 return hashList.yield();
472 }
473
474 CFDictionaryRef CodeDirectorySet::hashDict() const
475 {
476 CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary();
477
478 for (auto it = begin(); it != end(); ++it) {
479 SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first);
480
481 if (tag == SEC_OID_UNKNOWN) {
482 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
483 }
484
485 CFRef<CFNumberRef> hashType = makeCFNumber(int(tag));
486 CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash!
487 CFDictionarySetValue(hashDict, hashType, fullCdhash);
488 }
489
490 return hashDict.yield();
491 }
492
493 SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) {
494 SECOidTag tag;
495
496 switch (algorithm) {
497 case kSecCodeSignatureHashSHA1:
498 tag = SEC_OID_SHA1;
499 break;
500 case kSecCodeSignatureHashSHA256:
501 case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash
502 tag = SEC_OID_SHA256;
503 break;
504 case kSecCodeSignatureHashSHA384:
505 tag = SEC_OID_SHA384;
506 break;
507 default:
508 tag = SEC_OID_UNKNOWN;
509 }
510
511 return tag;
512 }
513
514
515
516 } // end namespace CodeSigning
517 } // end namespace Security