1/* 2 * Copyright (c) 2006-2010 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 31using namespace UnixPlusPlus; 32using LowLevelMemoryUtilities::alignUp; 33 34 35namespace Security { 36namespace CodeSigning { 37 38 39// 40// Create an (empty) builder 41// 42CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm) 43 : mFlags(0), 44 mHashType(digestAlgorithm), 45 mSpecialSlots(0), 46 mCodeSlots(0), 47 mScatter(NULL), 48 mScatterSize(0), 49 mDir(NULL) 50{ 51 mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength(); 52 mSpecial = (unsigned char *)calloc(cdSlotMax, mDigestLength); 53} 54 55CodeDirectory::Builder::~Builder() 56{ 57 ::free(mSpecial); 58 ::free(mScatter); 59} 60 61 62// 63// Set the source of the main executable (i.e. the code pages) 64// 65void CodeDirectory::Builder::executable(string path, 66 size_t pagesize, size_t offset, size_t length) 67{ 68 mExec.close(); // any previously opened one 69 mExec.open(path); 70 mPageSize = pagesize; 71 mExecOffset = offset; 72 mExecLength = length; 73} 74 75void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length) 76{ 77 assert(mExec); // already called executable() 78 mExec.close(); 79 mExec.open(path); 80 mExecOffset = offset; 81 mExecLength = length; 82} 83 84 85// 86// Set the source for one special slot 87// 88void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data) 89{ 90 assert(slot <= cdSlotMax); 91 MakeHash<Builder> hash(this); 92 hash->update(CFDataGetBytePtr(data), CFDataGetLength(data)); 93 hash->finish(specialSlot(slot)); 94 if (slot >= mSpecialSlots) 95 mSpecialSlots = slot; 96} 97 98 99// 100// Allocate a Scatter vector 101// 102CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count) 103{ 104 mScatterSize = (count + 1) * sizeof(Scatter); 105 if (!(mScatter = (Scatter *)::realloc(mScatter, mScatterSize))) 106 UnixError::throwMe(ENOMEM); 107 ::memset(mScatter, 0, mScatterSize); 108 return mScatter; 109} 110 111// This calculates the fixed size of the code directory 112// Because of <rdar://problem/16102695>, if the team ID 113// field is not used, we leave out the team ID offset 114// as well, to keep cd hashes consistent between 115// versions. 116const size_t CodeDirectory::Builder::fixedSize(const uint32_t version) 117{ 118 size_t cdSize = sizeof(CodeDirectory); 119 if (version < supportsTeamID) 120 cdSize -= sizeof(mDir->teamIDOffset); 121 122 return cdSize; 123} 124 125// 126// Calculate the size we'll need for the CodeDirectory as described so far 127// 128size_t CodeDirectory::Builder::size(const uint32_t version) 129{ 130 assert(mExec); // must have called executable() 131 if (mExecLength == 0) 132 mExecLength = mExec.fileSize() - mExecOffset; 133 134 // how many code pages? 135 if (mPageSize == 0) { // indefinite - one page 136 mCodeSlots = (mExecLength > 0); 137 } else { // finite - calculate from file size 138 mCodeSlots = (mExecLength + mPageSize - 1) / mPageSize; // round up 139 } 140 141 size_t offset = fixedSize(version); 142 143 offset += mScatterSize; // scatter vector 144 offset += mIdentifier.size() + 1; // size of identifier (with null byte) 145 if (mTeamID.size()) 146 offset += mTeamID.size() + 1; // size of teamID (with null byte) 147 offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector 148 149 return offset; 150} 151 152 153// 154// Take everything added to date and wrap it up in a shiny new CodeDirectory. 155// 156// Note that this only constructs a CodeDirectory; it does not touch any subsidiary 157// structures (resource tables, etc.), nor does it create any signature to secure 158// the CodeDirectory. 159// The returned CodeDirectory object is yours, and you may modify it as desired. 160// But the memory layout is set here, so the various sizes and counts should be good 161// when you call build(). 162// It's up to us to order the dynamic fields as we wish; but note that we currently 163// don't pad them, and so they should be allocated in non-increasing order of required 164// alignment. Make sure to keep the code here in sync with the size-calculating code above. 165// 166CodeDirectory *CodeDirectory::Builder::build() 167{ 168 assert(mExec); // must have (successfully) called executable() 169 uint32_t version; 170 171 // size and allocate 172 size_t identLength = mIdentifier.size() + 1; 173 size_t teamIDLength = mTeamID.size() + 1; 174 175 // Determine the version 176 if (mTeamID.size()) { 177 version = currentVersion; 178 } else { 179 version = supportsScatter; 180 } 181 182 size_t total = size(version); 183 if (!(mDir = (CodeDirectory *)calloc(1, total))) // initialize to zero 184 UnixError::throwMe(ENOMEM); 185 186 // fill header 187 mDir->initialize(total); 188 mDir->version = version; 189 mDir->flags = mFlags; 190 mDir->nSpecialSlots = (uint32_t)mSpecialSlots; 191 mDir->nCodeSlots = (uint32_t)mCodeSlots; 192 mDir->codeLimit = (uint32_t)mExecLength; 193 mDir->hashType = mHashType; 194 mDir->hashSize = mDigestLength; 195 if (mPageSize) { 196 int pglog; 197 assert(frexp(mPageSize, &pglog) == 0.5); // must be power of 2 198 frexp(mPageSize, &pglog); 199 assert(pglog < 256); 200 mDir->pageSize = pglog - 1; 201 } else 202 mDir->pageSize = 0; // means infinite page size 203 204 // locate and fill flex fields 205 size_t offset = fixedSize(mDir->version); 206 207 if (mScatter) { 208 mDir->scatterOffset = (uint32_t)offset; 209 memcpy(mDir->scatterVector(), mScatter, mScatterSize); 210 offset += mScatterSize; 211 } 212 213 mDir->identOffset = (uint32_t)offset; 214 memcpy(mDir->identifier(), mIdentifier.c_str(), identLength); 215 offset += identLength; 216 217 if (mTeamID.size()) { 218 mDir->teamIDOffset = (uint32_t)offset; 219 memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength); 220 offset += teamIDLength; 221 } 222 // (add new flexibly-allocated fields here) 223 224 mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength); 225 offset += (mSpecialSlots + mCodeSlots) * mDigestLength; 226 assert(offset == total); // matches allocated size 227 228 // fill special slots 229 memset((*mDir)[(int)-mSpecialSlots], 0, mDigestLength * mSpecialSlots); 230 for (size_t slot = 1; slot <= mSpecialSlots; ++slot) 231 memcpy((*mDir)[(int)-slot], specialSlot((SpecialSlot)slot), mDigestLength); 232 233 // fill code slots 234 mExec.seek(mExecOffset); 235 size_t remaining = mExecLength; 236 for (unsigned int slot = 0; slot < mCodeSlots; ++slot) { 237 size_t thisPage = min(mPageSize, remaining); 238 MakeHash<Builder> hasher(this); 239 generateHash(hasher, mExec, (*mDir)[slot], thisPage); 240 remaining -= thisPage; 241 } 242 243 // all done. Pass ownership to caller 244 return mDir; 245} 246 247 248} // CodeSigning 249} // Security 250