2 * Copyright (c) 2015 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 // diskimagerep - DiskRep representing a single read-only compressed disk image file
27 #include "diskimagerep.h"
28 #include "notarization.h"
30 #include "CodeSigner.h"
31 #include <security_utilities/endian.h>
36 namespace CodeSigning
{
40 using namespace UnixPlusPlus
;
43 static const int32_t udifVersion
= 4; // supported image file version
47 // Temporary hack to imply a fUDIFCryptosigFieldsset at the start of the "reserved" area of an UDIF header
49 bool DiskImageRep::readHeader(FileDesc
& fd
, UDIFFileHeader
& header
)
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
))
56 size_t headerOffset
= length
- sizeof(UDIFFileHeader
);
57 if (fd
.read(&header
, headerLength
, headerOffset
) != headerLength
)
59 if (n2h(header
.fUDIFSignature
) != kUDIFSignature
)
61 if (n2h(header
.fUDIFVersion
) != udifVersion
) // current as of this writing
71 DiskImageRep::DiskImageRep(const char *path
)
72 : SingleDiskRep(path
), mSigningData(NULL
)
77 DiskImageRep::~DiskImageRep()
79 free((void*)mSigningData
);
82 void DiskImageRep::setup()
84 free((void*)mSigningData
);
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
);
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
100 mEndOfDataOffset
= signatureOffset
;
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
)) {
110 MacOSError::throwMe(errSecCSBadDiskImageFormat
);
118 // The default binary identification of a SingleDiskRep is the (SHA-1) hash
119 // of the entire file itself.
121 CFDataRef
DiskImageRep::identification()
123 SHA1 hash
; // not security sensitive
124 hash(&mHeader
, sizeof(mHeader
));
127 return makeCFData(digest
, sizeof(digest
));
132 // Sniffer function for UDIF disk image files.
133 // This just looks for the trailing "header" and its magic number.
135 bool DiskImageRep::candidate(FileDesc
&fd
)
137 UDIFFileHeader header
;
138 return readHeader(fd
, header
) == true;
143 // Signing limit is the start of the (trailing) signature
145 size_t DiskImageRep::signingLimit()
147 return mEndOfDataOffset
;
150 void DiskImageRep::strictValidate(const CodeDirectory
* cd
, const ToleratedErrors
& tolerated
, SecCSFlags flags
)
152 DiskRep::strictValidate(cd
, tolerated
, flags
);
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
);
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.
169 CFDataRef
DiskImageRep::component(CodeDirectory::SpecialSlot slot
)
172 case cdRepSpecificSlot
:
173 return makeCFData(&mHeader
, sizeof(mHeader
));
175 return mSigningData
? mSigningData
->component(slot
) : NULL
;
181 // Provide a (vaguely) human readable characterization of this code
183 string
DiskImageRep::format()
188 void DiskImageRep::prepareForSigning(SigningContext
& context
)
190 // default to SHA256 unconditionally - we have no legacy issues to worry about
191 if (context
.digestAlgorithms().empty())
192 context
.setDigestAlgorithm(kSecCodeSignatureHashSHA256
);
197 // DiskImageRep::Writers
199 DiskRep::Writer
*DiskImageRep::writer()
201 return new Writer(this);
206 // Write a component.
208 void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
210 assert(slot
!= cdRepSpecificSlot
);
211 EmbeddedSignatureBlob::Maker::component(slot
, data
);
216 // Append the superblob we built to the cache file.
218 void DiskImageRep::Writer::flush()
220 free((void*)mSigningData
); // ditch previous blob just in case
221 mSigningData
= Maker::make(); // assemble new signature SuperBlob
223 // write signature superblob
224 size_t location
= rep
->mEndOfDataOffset
;
227 fd().writeAll(*mSigningData
); // write signature
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());
239 // Discretionary manipulations
241 void DiskImageRep::Writer::addDiscretionary(CodeDirectory::Builder
&builder
)
245 void DiskImageRep::registerStapledTicket()
247 CFRef
<CFDataRef
> data
= NULL
;
249 data
.take(mSigningData
->component(cdTicketSlot
));
250 registerStapledTicketInDMG(data
);
255 } // end namespace CodeSigning
256 } // end namespace Security