1/* 2 * Copyright (c) 2007 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// cfmdiskrep - single-file CFM (PEF) executable disk representation 26// 27#include "cfmdiskrep.h" 28#include <cstring> 29 30 31namespace Security { 32namespace CodeSigning { 33 34using namespace UnixPlusPlus; 35 36 37// 38// Everything's lazy in here 39// 40CFMDiskRep::CFMDiskRep(const char *path) 41 : SingleDiskRep(path), mTriedRead(false) 42{ 43 CODESIGN_DISKREP_CREATE_CFM(this, (char*)path); 44} 45 46CFMDiskRep::~CFMDiskRep() 47{ 48 if (mTriedRead) 49 delete mSigningData; 50} 51 52 53// 54// CFM filter heuristic. 55// We look for the PEF header within the first scanLength bytes 56// of the file's data fork, at certain alignment boundaries (probably 57// conservative). 58// 59bool CFMDiskRep::candidate(FileDesc &fd) 60{ 61 static const char magicMarker[] = "Joy!peffpwpc"; 62 static const size_t magicLength = 12; 63 static const size_t scanLength = 128; 64 static const size_t scanAlignment = 4; 65 66 char marker[scanLength]; 67 if (fd.read(marker, scanLength, 0) == scanLength) 68 for (size_t p = 0; p <= scanLength - magicLength; p += scanAlignment) 69 if (!memcmp(marker+p, magicMarker, magicLength)) 70 return true; 71 return false; 72} 73 74 75// 76// Extract and return a component by slot number. 77// If we have a Mach-O binary, use embedded components. 78// Otherwise, look for and return the extended attribute, if any. 79// 80CFDataRef CFMDiskRep::component(CodeDirectory::SpecialSlot slot) 81{ 82 if (!mTriedRead) 83 readSigningData(); 84 if (mSigningData) 85 return mSigningData->component(slot); 86 else 87 return NULL; 88} 89 90 91// 92// The signing limit is the start of the signature if present, 93// or the end of the file otherwise. 94// 95size_t CFMDiskRep::signingLimit() 96{ 97 readSigningData(); 98 if (mSigningData) 99 return mSigningOffset; 100 else 101 return fd().fileSize(); 102} 103 104 105// 106// Various other aspects of our DiskRep personality. 107// 108string CFMDiskRep::format() 109{ 110 return "CFM/PEF binary"; 111} 112 113 114// 115// Discard cached information 116// 117void CFMDiskRep::flush() 118{ 119 mTriedRead = false; 120 ::free(mSigningData); 121} 122 123 124// 125// In Mac OS X, a CFM binary must always be managed by the LaunchCFMApp 126// system tool. Thus, we recommend that this be required as a host. 127// 128static const uint8_t cfm_ireqs[] = { // host => anchor apple and identifier com.apple.LaunchCFMApp 129 0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 130 0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 131 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 132 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 133 0x43, 0x46, 0x4d, 0x41, 0x70, 0x70, 0x00, 0x00, 134}; 135 136const Requirements *CFMDiskRep::defaultRequirements(const Architecture *, const SigningContext &) 137{ 138 return ((const Requirements *)cfm_ireqs)->clone(); // need to pass ownership 139} 140 141 142// 143// Default to system-paged signing 144// 145size_t CFMDiskRep::pageSize(const SigningContext &) 146{ 147 return segmentedPageSize; 148} 149 150 151// 152// Locate, read, and cache embedded signing data from the CFM binary. 153// 154void CFMDiskRep::readSigningData() 155{ 156 if (!mTriedRead) { // try it once 157 mSigningData = NULL; // preset failure 158 mTriedRead = true; // we've tried (and perhaps failed) 159 160 FileDesc &fd = this->fd(); 161 fd.seek(fd.fileSize() - sizeof(Sentinel)); 162 Sentinel sentinel; 163 if (fd.read(&sentinel, sizeof(sentinel), fd.fileSize() - sizeof(Sentinel)) == sizeof(Sentinel)) 164 if (sentinel.magic == EmbeddedSignatureBlob::typeMagic) { 165 mSigningOffset = sentinel.offset; 166 if ((mSigningData = EmbeddedSignatureBlob::readBlob(fd, mSigningOffset))) 167 secdebug("cfmrep", "%zd signing bytes in %d blob(s) from %s(CFM)", 168 mSigningData->length(), mSigningData->count(), 169 mainExecutablePath().c_str()); 170 else 171 secdebug("cfmrep", "failed to read signing bytes from %s(CFM)", 172 mainExecutablePath().c_str()); 173 } 174 } 175} 176 177 178// 179// CFMDiskRep::Writers 180// 181DiskRep::Writer *CFMDiskRep::writer() 182{ 183 return new Writer(this); 184} 185 186CFMDiskRep::Writer::~Writer() 187{ 188 delete mSigningData; 189} 190 191 192// 193// Write a component. 194// Note that this isn't concerned with Mach-O writing; this is handled at 195// a much higher level. If we're called, it's extended attribute time. 196// 197void CFMDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 198{ 199 EmbeddedSignatureBlob::Maker::component(slot, data); 200} 201 202 203// 204// Append the superblob we built to the CFM binary. 205// Note: Aligning the signing blob to a 16-byte boundary is not strictly necessary, 206// but it's what the Mach-O case does, and it probably improves performance a bit. 207// 208void CFMDiskRep::Writer::flush() 209{ 210 delete mSigningData; // ditch previous blob just in case 211 mSigningData = Maker::make(); // assemble new signature SuperBlob 212 size_t start = LowLevelMemoryUtilities::alignUp(rep->signingLimit(), 16); 213 Sentinel sentinel; 214 sentinel.magic = EmbeddedSignatureBlob::typeMagic; 215 sentinel.offset = (uint32_t)start; 216 AutoFileDesc fd(rep->path(), O_RDWR); 217 fd.seek(start); 218 fd.writeAll(mSigningData, mSigningData->length()); 219 fd.writeAll(&sentinel, sizeof(sentinel)); 220} 221 222 223} // end namespace CodeSigning 224} // end namespace Security 225