2  * Copyright (c) 2006-2012,2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25 // cdbuilder - constructor for CodeDirectories 
  27 #include "cdbuilder.h" 
  28 #include <security_utilities/memutils.h> 
  31 using namespace UnixPlusPlus
; 
  32 using LowLevelMemoryUtilities::alignUp
; 
  36 namespace CodeSigning 
{ 
  40 // Create an (empty) builder 
  42 CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm
) 
  44           mHashType(digestAlgorithm
), 
  52         mDigestLength 
= (uint32_t)MakeHash
<Builder
>(this)->digestLength(); 
  53         mSpecial 
= (unsigned char *)calloc(cdSlotMax
, mDigestLength
); 
  56 CodeDirectory::Builder::~Builder() 
  64 // Set the source of the main executable (i.e. the code pages) 
  66 void CodeDirectory::Builder::executable(string path
, 
  67         size_t pagesize
, size_t offset
, size_t length
) 
  69         mExec
.close();                  // any previously opened one 
  76 void CodeDirectory::Builder::reopen(string path
, size_t offset
, size_t length
) 
  78         assert(mExec
);                                  // already called executable() 
  87 // Set the source for one special slot 
  89 void CodeDirectory::Builder::specialSlot(SpecialSlot slot
, CFDataRef data
) 
  91         assert(slot 
<= cdSlotMax
); 
  92         MakeHash
<Builder
> hash(this); 
  93         hash
->update(CFDataGetBytePtr(data
), CFDataGetLength(data
)); 
  94         hash
->finish(specialSlot(slot
)); 
  95         mFilledSpecialSlots
.insert(slot
); 
  96         if (slot 
>= mSpecialSlots
) 
 102 // Allocate a Scatter vector 
 104 CodeDirectory::Scatter 
*CodeDirectory::Builder::scatter(unsigned count
) 
 106         mScatterSize 
= (count 
+ 1) * sizeof(Scatter
); 
 107         if (!(mScatter 
= (Scatter 
*)::realloc(mScatter
, mScatterSize
))) 
 108                 UnixError::throwMe(ENOMEM
); 
 109         ::memset(mScatter
, 0, mScatterSize
); 
 114 // Keep the allocated size of the (static) CodeDirectory consistent with 
 115 // the version chosen. We dynamically picked the least-needed version 
 116 // to provide stability of virtual signatures. 
 118 size_t CodeDirectory::Builder::fixedSize(const uint32_t version
) 
 120         size_t cdSize 
= sizeof(CodeDirectory
); 
 121         if (version 
< supportsCodeLimit64
) 
 122                 cdSize 
-= sizeof(mDir
->spare3
) + sizeof(mDir
->codeLimit64
); 
 123         if (version 
< supportsTeamID
) 
 124                 cdSize 
-= sizeof(mDir
->teamIDOffset
); 
 130 // Calculate the size we'll need for the CodeDirectory as described so far 
 132 size_t CodeDirectory::Builder::size(const uint32_t version
) 
 134         assert(mExec
);                  // must have called executable() 
 135         if (mExecLength 
== 0) 
 136                 mExecLength 
= mExec
.fileSize() - mExecOffset
; 
 138         // how many code pages? 
 139         if (mExecLength 
<= 0) { // no code, no slots 
 141         } else if (mPageSize 
== 0) {    // indefinite - one page 
 143         } else {                                // finite - calculate from file size 
 144                 mCodeSlots 
= (mExecLength 
- 1) / mPageSize 
+ 1; 
 147         size_t offset 
= fixedSize(version
); 
 148         size_t offset0 
= offset
; 
 150         offset 
+= mScatterSize
;                         // scatter vector 
 151         offset 
+= mIdentifier
.size() + 1;       // size of identifier (with null byte) 
 153                 offset 
+= mTeamID
.size() + 1;   // size of teamID (with null byte) 
 154         offset 
+= (mCodeSlots 
+ mSpecialSlots
) * mDigestLength
; // hash vector 
 155         if (offset 
<= offset0
) 
 156                 UnixError::throwMe(ENOEXEC
); 
 163 // Take everything added to date and wrap it up in a shiny new CodeDirectory. 
 165 // Note that this only constructs a CodeDirectory; it does not touch any subsidiary 
 166 // structures (resource tables, etc.), nor does it create any signature to secure 
 167 // the CodeDirectory. 
 168 // The returned CodeDirectory object is yours, and you may modify it as desired. 
 169 // But the memory layout is set here, so the various sizes and counts should be good 
 170 // when you call build(). 
 171 // It's up to us to order the dynamic fields as we wish; but note that we currently 
 172 // don't pad them, and so they should be allocated in non-increasing order of required 
 173 // alignment. Make sure to keep the code here in sync with the size-calculating code above. 
 175 CodeDirectory 
*CodeDirectory::Builder::build() 
 177         assert(mExec
);                  // must have (successfully) called executable() 
 181         size_t identLength 
= mIdentifier
.size() + 1; 
 182         size_t teamIDLength 
= mTeamID
.size() + 1; 
 184         // Determine the version 
 185         if (mExecLength 
> UINT32_MAX
) { 
 186                 version 
= currentVersion
; 
 187         } else if (mTeamID
.size()) { 
 188                 version 
= supportsTeamID
; 
 190                 version 
= supportsScatter
; 
 193         if (mCodeSlots 
> UINT32_MAX
)    // (still limited to 32 bits) 
 194                 MacOSError::throwMe(errSecCSTooBig
); 
 196         size_t total 
= size(version
); 
 197         if (!(mDir 
= (CodeDirectory 
*)calloc(1, total
)))        // initialize to zero 
 198                 UnixError::throwMe(ENOMEM
); 
 201         mDir
->initialize(total
); 
 202         mDir
->version 
= version
; 
 203         mDir
->flags 
= mFlags
; 
 204         mDir
->nSpecialSlots 
= (uint32_t)mSpecialSlots
; 
 205         mDir
->nCodeSlots 
= (uint32_t)mCodeSlots
; 
 206         if (mExecLength 
> UINT32_MAX
) { 
 207                 mDir
->codeLimit 
= UINT32_MAX
; 
 208                 mDir
->codeLimit64 
= mExecLength
; 
 210                 mDir
->codeLimit 
= uint32_t(mExecLength
); 
 212         mDir
->hashType 
= mHashType
; 
 213         mDir
->platform 
= mPlatform
; 
 214         mDir
->hashSize 
= mDigestLength
; 
 217                 assert(frexp(mPageSize
, &pglog
) == 0.5); // must be power of 2 
 218                 frexp(mPageSize
, &pglog
); 
 220                 mDir
->pageSize 
= pglog 
- 1; 
 222                 mDir
->pageSize 
= 0;     // means infinite page size 
 224         // locate and fill flex fields 
 225         size_t offset 
= fixedSize(mDir
->version
); 
 228                 mDir
->scatterOffset 
= (uint32_t)offset
; 
 229                 memcpy(mDir
->scatterVector(), mScatter
, mScatterSize
); 
 230                 offset 
+= mScatterSize
; 
 233         mDir
->identOffset 
= (uint32_t)offset
; 
 234         memcpy(mDir
->identifier(), mIdentifier
.c_str(), identLength
); 
 235         offset 
+= identLength
; 
 237         if (mTeamID
.size()) { 
 238                 mDir
->teamIDOffset 
= (uint32_t)offset
; 
 239                 memcpy(mDir
->teamID(), mTeamID
.c_str(), teamIDLength
); 
 240                 offset 
+= teamIDLength
; 
 242         // (add new flexibly-allocated fields here) 
 244         mDir
->hashOffset 
= (uint32_t)(offset 
+ mSpecialSlots 
* mDigestLength
); 
 245         offset 
+= (mSpecialSlots 
+ mCodeSlots
) * mDigestLength
; 
 246         assert(offset 
== total
);        // matches allocated size 
 248         // fill special slots 
 249         memset((*mDir
)[(int)-mSpecialSlots
], 0, mDigestLength 
* mSpecialSlots
); 
 250         for (size_t slot 
= 1; slot 
<= mSpecialSlots
; ++slot
) 
 251                 memcpy((*mDir
)[(int)-slot
], specialSlot((SpecialSlot
)slot
), mDigestLength
); 
 254         mExec
.seek(mExecOffset
); 
 255         size_t remaining 
= mExecLength
; 
 256         for (unsigned int slot 
= 0; slot 
< mCodeSlots
; ++slot
) { 
 257                 size_t thisPage 
= remaining
; 
 259                         thisPage 
= min(thisPage
, mPageSize
); 
 260                 MakeHash
<Builder
> hasher(this); 
 261                 generateHash(hasher
, mExec
, (*mDir
)[slot
], thisPage
); 
 262                 remaining 
-= thisPage
; 
 264         assert(remaining 
== 0); 
 266         // all done. Pass ownership to caller