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
)
77 void DiskImageRep::setup()
81 // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment
82 if (!readHeader(fd(), this->mHeader
))
83 UnixError::throwMe(errSecCSBadDiskImageFormat
);
85 mHeaderOffset
= fd().fileSize() - sizeof(UDIFFileHeader
);
86 size_t signatureOffset
= size_t(n2h(this->mHeader
.fUDIFCodeSignOffset
));
87 size_t signatureLength
= size_t(n2h(this->mHeader
.fUDIFCodeSignLength
));
88 this->mHeader
.fUDIFCodeSignLength
= 0; // blind length (signature covers header)
89 if (signatureOffset
== 0) {
90 mEndOfDataOffset
= mHeaderOffset
;
91 mHeader
.fUDIFCodeSignOffset
= h2n(mHeaderOffset
);
92 return; // unsigned, header prepared for possible signing
94 mEndOfDataOffset
= signatureOffset
;
97 // read the signature superblob
98 const size_t frameLength
= mHeaderOffset
- signatureOffset
; // room to following header
99 if (EmbeddedSignatureBlob
* blob
= EmbeddedSignatureBlob::readBlob(fd(), signatureOffset
, frameLength
)) {
100 if (blob
->length() != frameLength
101 || frameLength
!= signatureLength
102 || !blob
->strictValidateBlob(frameLength
)) {
104 MacOSError::throwMe(errSecCSBadDiskImageFormat
);
112 // The default binary identification of a SingleDiskRep is the (SHA-1) hash
113 // of the entire file itself.
115 CFDataRef
DiskImageRep::identification()
117 SHA1 hash
; // not security sensitive
118 hash(&mHeader
, sizeof(mHeader
));
121 return makeCFData(digest
, sizeof(digest
));
126 // Sniffer function for UDIF disk image files.
127 // This just looks for the trailing "header" and its magic number.
129 bool DiskImageRep::candidate(FileDesc
&fd
)
131 UDIFFileHeader header
;
132 return readHeader(fd
, header
) == true;
137 // Signing limit is the start of the (trailing) signature
139 size_t DiskImageRep::signingLimit()
141 return mEndOfDataOffset
;
144 void DiskImageRep::strictValidate(const CodeDirectory
* cd
, const ToleratedErrors
& tolerated
, SecCSFlags flags
)
146 DiskRep::strictValidate(cd
, tolerated
, flags
);
149 size_t cd_limit
= cd
->signingLimit();
150 size_t dr_limit
= signingLimit();
151 if (cd_limit
!= dr_limit
&& // must cover exactly the entire data
152 cd_limit
!= fd().fileSize()) // or, for legacy detached sigs, the entire file
153 MacOSError::throwMe(errSecCSSignatureInvalid
);
159 // Retrieve a component from the executable.
160 // Our mCache has mapped the entire file, so we just fish the contents out of
161 // the mapped area as needed.
163 CFDataRef
DiskImageRep::component(CodeDirectory::SpecialSlot slot
)
166 case cdRepSpecificSlot
:
167 return makeCFData(&mHeader
, sizeof(mHeader
));
169 return mSigningData
? mSigningData
->component(slot
) : NULL
;
175 // Provide a (vaguely) human readable characterization of this code
177 string
DiskImageRep::format()
182 void DiskImageRep::prepareForSigning(SigningContext
& context
)
184 // default to SHA256 unconditionally - we have no legacy issues to worry about
185 if (context
.digestAlgorithms().empty())
186 context
.setDigestAlgorithm(kSecCodeSignatureHashSHA256
);
191 // DiskImageRep::Writers
193 DiskRep::Writer
*DiskImageRep::writer()
195 return new Writer(this);
200 // Write a component.
202 void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
204 assert(slot
!= cdRepSpecificSlot
);
205 EmbeddedSignatureBlob::Maker::component(slot
, data
);
210 // Append the superblob we built to the cache file.
212 void DiskImageRep::Writer::flush()
214 delete mSigningData
; // ditch previous blob just in case
215 mSigningData
= Maker::make(); // assemble new signature SuperBlob
217 // write signature superblob
218 size_t location
= rep
->mEndOfDataOffset
;
221 fd().writeAll(*mSigningData
); // write signature
223 // now (re)write disk image header after it
224 UDIFFileHeader fullHeader
= rep
->mHeader
;
225 fullHeader
.fUDIFCodeSignOffset
= h2n(location
);
226 fullHeader
.fUDIFCodeSignLength
= h2n(mSigningData
->length());
227 fd().writeAll(&fullHeader
, sizeof(rep
->mHeader
));
228 fd().truncate(fd().position());
233 // Discretionary manipulations
235 void DiskImageRep::Writer::addDiscretionary(CodeDirectory::Builder
&builder
)
239 void DiskImageRep::registerStapledTicket()
241 CFRef
<CFDataRef
> data
= NULL
;
243 data
.take(mSigningData
->component(cdTicketSlot
));
244 registerStapledTicketInDMG(data
);
249 } // end namespace CodeSigning
250 } // end namespace Security