cdbuilder.cpp
1 /* 2 * Copyright (c) 2006-2012,2014 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 // cdbuilder - constructor for CodeDirectories 26 // 27 #include "cdbuilder.h" 28 #include <security_utilities/memutils.h> 29 #include <cmath> 30 31 using namespace UnixPlusPlus; 32 using LowLevelMemoryUtilities::alignUp; 33 34 35 namespace Security { 36 namespace CodeSigning { 37 38 39 // 40 // Create an (empty) builder 41 // 42 CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm) 43 : mFlags(0), 44 mHashType(digestAlgorithm), 45 mPlatform(0), 46 mSpecialSlots(0), 47 mCodeSlots(0), 48 mScatter(NULL), 49 mScatterSize(0), 50 mExecSegOffset(0), 51 mExecSegLimit(0), 52 mExecSegFlags(0), 53 mGeneratePreEncryptHashes(false), 54 mRuntimeVersion(0), 55 mDir(NULL) 56 { 57 mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength(); 58 mSpecial = (unsigned char *)calloc(cdSlotMax, mDigestLength); 59 } 60 61 CodeDirectory::Builder::~Builder() 62 { 63 ::free(mSpecial); 64 ::free(mScatter); 65 } 66 67 68 // 69 // Set the source of the main executable (i.e. the code pages) 70 // 71 void CodeDirectory::Builder::executable(string path, 72 size_t pagesize, size_t offset, size_t length) 73 { 74 mExec.close(); // any previously opened one 75 mExec.open(path); 76 mPageSize = pagesize; 77 mExecOffset = offset; 78 mExecLength = length; 79 } 80 81 void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length) 82 { 83 assert(opened()); // already called executable() 84 mExec.close(); 85 mExec.open(path); 86 mExecOffset = offset; 87 mExecLength = length; 88 } 89 90 bool CodeDirectory::Builder::opened() 91 { 92 return bool(mExec); 93 } 94 95 96 // 97 // Set the source for one special slot 98 // 99 void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data) 100 { 101 assert(slot <= cdSlotMax); 102 MakeHash<Builder> hash(this); 103 hash->update(CFDataGetBytePtr(data), CFDataGetLength(data)); 104 hash->finish(specialSlot(slot)); 105 mFilledSpecialSlots.insert(slot); 106 if (slot >= mSpecialSlots) 107 mSpecialSlots = slot; 108 } 109 110 111 // 112 // Allocate a Scatter vector 113 // 114 CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count) 115 { 116 mScatterSize = (count + 1) * sizeof(Scatter); 117 if (!(mScatter = (Scatter *)::realloc(mScatter, mScatterSize))) 118 UnixError::throwMe(ENOMEM); 119 ::memset(mScatter, 0, mScatterSize); 120 return mScatter; 121 } 122 123 // 124 // Keep the allocated size of the (static) CodeDirectory consistent with 125 // the version chosen. We dynamically picked the least-needed version 126 // to provide stability of virtual signatures. 127 // 128 size_t CodeDirectory::Builder::fixedSize(const uint32_t version) 129 { 130 size_t cdSize = sizeof(CodeDirectory); 131 if (version < supportsPreEncrypt) 132 cdSize -= sizeof(mDir->runtime) + sizeof(mDir->preEncryptOffset); 133 if (version < supportsExecSegment) 134 cdSize -= sizeof(mDir->execSegBase) + sizeof(mDir->execSegLimit) + sizeof(mDir->execSegFlags); 135 if (version < supportsCodeLimit64) 136 cdSize -= sizeof(mDir->spare3) + sizeof(mDir->codeLimit64); 137 if (version < supportsTeamID) 138 cdSize -= sizeof(mDir->teamIDOffset); 139 140 return cdSize; 141 } 142 143 // 144 // Calculate the size we'll need for the CodeDirectory as described so far 145 // 146 size_t CodeDirectory::Builder::size(const uint32_t version) 147 { 148 assert(mExec); // must have called executable() 149 if (mExecLength == 0) 150 mExecLength = mExec.fileSize() - mExecOffset; 151 152 // how many code pages? 153 if (mExecLength <= 0) { // no code, no slots 154 mCodeSlots = 0; 155 } else if (mPageSize == 0) { // indefinite - one page 156 mCodeSlots = 1; 157 } else { // finite - calculate from file size 158 mCodeSlots = (mExecLength - 1) / mPageSize + 1; 159 } 160 161 size_t offset = fixedSize(version); 162 size_t offset0 = offset; 163 164 offset += mScatterSize; // scatter vector 165 offset += mIdentifier.size() + 1; // size of identifier (with null byte) 166 if (mTeamID.size()) 167 offset += mTeamID.size() + 1; // size of teamID (with null byte) 168 offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector 169 170 if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) { 171 offset += mCodeSlots * mDigestLength; 172 } 173 174 if (offset <= offset0) 175 UnixError::throwMe(ENOEXEC); 176 177 return offset; 178 } 179 180 181 // 182 // Take everything added to date and wrap it up in a shiny new CodeDirectory. 183 // 184 // Note that this only constructs a CodeDirectory; it does not touch any subsidiary 185 // structures (resource tables, etc.), nor does it create any signature to secure 186 // the CodeDirectory. 187 // The returned CodeDirectory object is yours, and you may modify it as desired. 188 // But the memory layout is set here, so the various sizes and counts should be good 189 // when you call build(). 190 // It's up to us to order the dynamic fields as we wish; but note that we currently 191 // don't pad them, and so they should be allocated in non-increasing order of required 192 // alignment. Make sure to keep the code here in sync with the size-calculating code above. 193 // 194 CodeDirectory *CodeDirectory::Builder::build() 195 { 196 assert(mExec); // must have (successfully) called executable() 197 uint32_t version; 198 199 // size and allocate 200 size_t identLength = mIdentifier.size() + 1; 201 size_t teamIDLength = mTeamID.size() + 1; 202 203 // Determine the version 204 if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty() || mRuntimeVersion) { 205 version = currentVersion; 206 } else if (mExecSegLimit > 0) { 207 version = supportsExecSegment; 208 } else if (mExecLength > UINT32_MAX) { 209 version = supportsCodeLimit64; 210 } else if (mTeamID.size()) { 211 version = supportsTeamID; 212 } else { 213 version = supportsScatter; 214 } 215 216 if (mCodeSlots > UINT32_MAX) // (still limited to 32 bits) 217 MacOSError::throwMe(errSecCSTooBig); 218 219 size_t total = size(version); 220 if (!(mDir = (CodeDirectory *)calloc(1, total))) // initialize to zero 221 UnixError::throwMe(ENOMEM); 222 223 // fill header 224 mDir->initialize(total); 225 mDir->version = version; 226 mDir->flags = mFlags; 227 mDir->nSpecialSlots = (uint32_t)mSpecialSlots; 228 mDir->nCodeSlots = (uint32_t)mCodeSlots; 229 if (mExecLength > UINT32_MAX) { 230 mDir->codeLimit = UINT32_MAX; 231 mDir->codeLimit64 = mExecLength; 232 } else { 233 mDir->codeLimit = uint32_t(mExecLength); 234 } 235 mDir->hashType = mHashType; 236 mDir->platform = mPlatform; 237 mDir->hashSize = mDigestLength; 238 if (mPageSize) { 239 int pglog; 240 assert(frexp(mPageSize, &pglog) == 0.5); // must be power of 2 241 frexp(mPageSize, &pglog); 242 assert(pglog < 256); 243 mDir->pageSize = pglog - 1; 244 } else 245 mDir->pageSize = 0; // means infinite page size 246 247 mDir->execSegBase = mExecSegOffset; 248 mDir->execSegLimit = mExecSegLimit; 249 mDir->execSegFlags = mExecSegFlags; 250 mDir->runtime = mRuntimeVersion; 251 252 // locate and fill flex fields 253 size_t offset = fixedSize(mDir->version); 254 255 if (mScatter) { 256 mDir->scatterOffset = (uint32_t)offset; 257 memcpy(mDir->scatterVector(), mScatter, mScatterSize); 258 offset += mScatterSize; 259 } 260 261 mDir->identOffset = (uint32_t)offset; 262 memcpy(mDir->identifier(), mIdentifier.c_str(), identLength); 263 offset += identLength; 264 265 if (mTeamID.size()) { 266 mDir->teamIDOffset = (uint32_t)offset; 267 memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength); 268 offset += teamIDLength; 269 } 270 271 // (add new flexibly-allocated fields here) 272 273 /* Pre-encrypt hashes come before normal hashes, so that the kernel can free 274 * the normal, potentially post-encrypt hashes away easily. */ 275 if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) { 276 mDir->preEncryptOffset = (uint32_t)offset; 277 offset += mCodeSlots * mDigestLength; 278 } 279 280 mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength); 281 offset += (mSpecialSlots + mCodeSlots) * mDigestLength; 282 283 assert(offset == total); // matches allocated size 284 285 (void)offset; 286 287 // fill special slots 288 memset(mDir->getSlotMutable((int)-mSpecialSlots, false), 0, mDigestLength * mSpecialSlots); 289 for (size_t slot = 1; slot <= mSpecialSlots; ++slot) 290 memcpy(mDir->getSlotMutable((int)-slot, false), specialSlot((SpecialSlot)slot), mDigestLength); 291 292 // fill code slots 293 mExec.seek(mExecOffset); 294 size_t remaining = mExecLength; 295 for (unsigned int slot = 0; slot < mCodeSlots; ++slot) { 296 size_t thisPage = remaining; 297 if (mPageSize) 298 thisPage = min(thisPage, mPageSize); 299 MakeHash<Builder> hasher(this); 300 generateHash(hasher, mExec, mDir->getSlotMutable(slot, false), thisPage); 301 if (mGeneratePreEncryptHashes && mPreservedPreEncryptHashMap.empty()) { 302 memcpy(mDir->getSlotMutable(slot, true), mDir->getSlot(slot, false), 303 mDir->hashSize); 304 } 305 remaining -= thisPage; 306 } 307 assert(remaining == 0); 308 309 PreEncryptHashMap::iterator preEncrypt = 310 mPreservedPreEncryptHashMap.find(mHashType); 311 if (preEncrypt != mPreservedPreEncryptHashMap.end()) { 312 memcpy(mDir->getSlotMutable(0, true), 313 CFDataGetBytePtr(preEncrypt->second), 314 mCodeSlots * mDigestLength); 315 mPreservedPreEncryptHashMap.erase(preEncrypt->first); // Releases the CFData memory. 316 } 317 318 // all done. Pass ownership to caller 319 return mDir; 320 } 321 322 323 } // CodeSigning 324 } // Security