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/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 45namespace Security { 46namespace CodeSigning { 47 48 49// 50// About the Mach-O allocation helper 51// 52static const char helperName[] = "codesign_allocate"; 53static const char helperPath[] = "/usr/bin/codesign_allocate"; 54static const char helperOverride[] = "CODESIGN_ALLOCATE"; 55static const size_t csAlign = 16; 56 57 58// 59// BlobWriters 60// 61void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 62{ 63 return EmbeddedSignatureBlob::Maker::component(slot, data); 64} 65 66 67void 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// 79ArchEditor::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 90ArchEditor::~ArchEditor() 91{ 92 for (ArchMap::iterator it = begin(); it != end(); ++it) 93 delete it->second; 94} 95 96 97// 98// BlobEditor 99// 100BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s) 101 : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s) 102{ } 103 104 105void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 106{ 107 mGlobal.component(slot, data); 108} 109 110void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) 111{ 112 mMaker.add(arch.architecture.cpuType(), blob); 113} 114 115 116void 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// 133MachOEditor::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 150MachOEditor::~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 159void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 160{ 161 writer->component(slot, data); 162} 163 164 165void 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 184static 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 194void 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 205void 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 242void 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// 254void 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// 277void 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// 311void 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// 339PreSigningContext::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 362