diskimagerep.cpp
1 /* 2 * Copyright (c) 2015 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 // diskimagerep - DiskRep representing a single read-only compressed disk image file 26 // 27 #include "diskimagerep.h" 28 #include "notarization.h" 29 #include "sigblob.h" 30 #include "CodeSigner.h" 31 #include <security_utilities/endian.h> 32 #include <algorithm> 33 34 35 namespace Security { 36 namespace CodeSigning { 37 38 using Security::n2h; 39 using Security::h2n; 40 using namespace UnixPlusPlus; 41 42 43 static const int32_t udifVersion = 4; // supported image file version 44 45 46 // 47 // Temporary hack to imply a fUDIFCryptosigFieldsset at the start of the "reserved" area of an UDIF header 48 // 49 bool DiskImageRep::readHeader(FileDesc& fd, UDIFFileHeader& header) 50 { 51 // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment 52 static const size_t headerLength = sizeof(header); 53 size_t length = fd.fileSize(); 54 if (length < sizeof(UDIFFileHeader) + sizeof(BlobCore)) 55 return false; 56 size_t headerOffset = length - sizeof(UDIFFileHeader); 57 if (fd.read(&header, headerLength, headerOffset) != headerLength) 58 return false; 59 if (n2h(header.fUDIFSignature) != kUDIFSignature) 60 return false; 61 if (n2h(header.fUDIFVersion) != udifVersion) // current as of this writing 62 return false; 63 64 return true; 65 } 66 67 68 // 69 // Object management. 70 // 71 DiskImageRep::DiskImageRep(const char *path) 72 : SingleDiskRep(path), mSigningData(NULL) 73 { 74 this->setup(); 75 } 76 77 DiskImageRep::~DiskImageRep() 78 { 79 free((void*)mSigningData); 80 } 81 82 void DiskImageRep::setup() 83 { 84 free((void*)mSigningData); 85 mSigningData = NULL; 86 87 // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment 88 if (!readHeader(fd(), this->mHeader)) 89 UnixError::throwMe(errSecCSBadDiskImageFormat); 90 91 mHeaderOffset = fd().fileSize() - sizeof(UDIFFileHeader); 92 size_t signatureOffset = size_t(n2h(this->mHeader.fUDIFCodeSignOffset)); 93 size_t signatureLength = size_t(n2h(this->mHeader.fUDIFCodeSignLength)); 94 this->mHeader.fUDIFCodeSignLength = 0; // blind length (signature covers header) 95 if (signatureOffset == 0) { 96 mEndOfDataOffset = mHeaderOffset; 97 mHeader.fUDIFCodeSignOffset = h2n(mHeaderOffset); 98 return; // unsigned, header prepared for possible signing 99 } else { 100 mEndOfDataOffset = signatureOffset; 101 } 102 103 // read the signature superblob 104 const size_t frameLength = mHeaderOffset - signatureOffset; // room to following header 105 if (EmbeddedSignatureBlob* blob = EmbeddedSignatureBlob::readBlob(fd(), signatureOffset, frameLength)) { 106 if (blob->length() != frameLength 107 || frameLength != signatureLength 108 || !blob->strictValidateBlob(frameLength)) { 109 free(blob); 110 MacOSError::throwMe(errSecCSBadDiskImageFormat); 111 } 112 mSigningData = blob; 113 } 114 } 115 116 117 // 118 // The default binary identification of a SingleDiskRep is the (SHA-1) hash 119 // of the entire file itself. 120 // 121 CFDataRef DiskImageRep::identification() 122 { 123 SHA1 hash; // not security sensitive 124 hash(&mHeader, sizeof(mHeader)); 125 SHA1::Digest digest; 126 hash.finish(digest); 127 return makeCFData(digest, sizeof(digest)); 128 } 129 130 131 // 132 // Sniffer function for UDIF disk image files. 133 // This just looks for the trailing "header" and its magic number. 134 // 135 bool DiskImageRep::candidate(FileDesc &fd) 136 { 137 UDIFFileHeader header; 138 return readHeader(fd, header) == true; 139 } 140 141 142 // 143 // Signing limit is the start of the (trailing) signature 144 // 145 size_t DiskImageRep::signingLimit() 146 { 147 return mEndOfDataOffset; 148 } 149 150 void DiskImageRep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags) 151 { 152 DiskRep::strictValidate(cd, tolerated, flags); 153 154 if (cd) { 155 size_t cd_limit = cd->signingLimit(); 156 size_t dr_limit = signingLimit(); 157 if (cd_limit != dr_limit && // must cover exactly the entire data 158 cd_limit != fd().fileSize()) // or, for legacy detached sigs, the entire file 159 MacOSError::throwMe(errSecCSSignatureInvalid); 160 } 161 } 162 163 164 // 165 // Retrieve a component from the executable. 166 // Our mCache has mapped the entire file, so we just fish the contents out of 167 // the mapped area as needed. 168 // 169 CFDataRef DiskImageRep::component(CodeDirectory::SpecialSlot slot) 170 { 171 switch (slot) { 172 case cdRepSpecificSlot: 173 return makeCFData(&mHeader, sizeof(mHeader)); 174 default: 175 return mSigningData ? mSigningData->component(slot) : NULL; 176 } 177 } 178 179 180 // 181 // Provide a (vaguely) human readable characterization of this code 182 // 183 string DiskImageRep::format() 184 { 185 return "disk image"; 186 } 187 188 void DiskImageRep::prepareForSigning(SigningContext& context) 189 { 190 // default to SHA256 unconditionally - we have no legacy issues to worry about 191 if (context.digestAlgorithms().empty()) 192 context.setDigestAlgorithm(kSecCodeSignatureHashSHA256); 193 } 194 195 196 // 197 // DiskImageRep::Writers 198 // 199 DiskRep::Writer *DiskImageRep::writer() 200 { 201 return new Writer(this); 202 } 203 204 205 // 206 // Write a component. 207 // 208 void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 209 { 210 assert(slot != cdRepSpecificSlot); 211 EmbeddedSignatureBlob::Maker::component(slot, data); 212 } 213 214 215 // 216 // Append the superblob we built to the cache file. 217 // 218 void DiskImageRep::Writer::flush() 219 { 220 free((void*)mSigningData); // ditch previous blob just in case 221 mSigningData = Maker::make(); // assemble new signature SuperBlob 222 223 // write signature superblob 224 size_t location = rep->mEndOfDataOffset; 225 assert(location); 226 fd().seek(location); 227 fd().writeAll(*mSigningData); // write signature 228 229 // now (re)write disk image header after it 230 UDIFFileHeader fullHeader = rep->mHeader; 231 fullHeader.fUDIFCodeSignOffset = h2n(location); 232 fullHeader.fUDIFCodeSignLength = h2n(mSigningData->length()); 233 fd().writeAll(&fullHeader, sizeof(rep->mHeader)); 234 fd().truncate(fd().position()); 235 } 236 237 238 // 239 // Discretionary manipulations 240 // 241 void DiskImageRep::Writer::addDiscretionary(CodeDirectory::Builder &builder) 242 { 243 } 244 245 void DiskImageRep::registerStapledTicket() 246 { 247 CFRef<CFDataRef> data = NULL; 248 if (mSigningData) { 249 data.take(mSigningData->component(cdTicketSlot)); 250 registerStapledTicketInDMG(data); 251 } 252 } 253 254 255 } // end namespace CodeSigning 256 } // end namespace Security