/ OSX / libsecurity_codesigning / lib / signerutils.cpp
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