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"
29 #include "CodeSigner.h"
30 #include <security_utilities/endian.h>
35 namespace CodeSigning
{
39 using namespace UnixPlusPlus
;
42 static const int32_t udifVersion
= 4; // supported image file version
46 // Temporary hack to imply a fUDIFCryptosigFieldsset at the start of the "reserved" area of an UDIF header
49 uint64_t fCodeSignatureOffset
;
50 uint64_t fCodeSignatureLength
;
53 UDIFSigning
& sigFields(UDIFFileHeader
& header
);
54 const UDIFSigning
& sigFields(const UDIFFileHeader
& header
);
56 UDIFSigning
& sigFields(UDIFFileHeader
& header
)
58 return *(UDIFSigning
*)&header
.fReserved
;
61 const UDIFSigning
& sigFields(const UDIFFileHeader
& header
)
63 return *(UDIFSigning
*)&header
.fReserved
;
66 bool DiskImageRep::readHeader(FileDesc
& fd
, UDIFFileHeader
& header
)
68 // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment
69 static const size_t headerLength
= sizeof(header
);
70 size_t length
= fd
.fileSize();
71 if (length
< sizeof(UDIFFileHeader
) + sizeof(BlobCore
))
73 size_t headerOffset
= length
- sizeof(UDIFFileHeader
);
74 if (fd
.read(&header
, headerLength
, headerOffset
) != headerLength
)
76 if (n2h(header
.fUDIFSignature
) != kUDIFSignature
)
78 if (n2h(header
.fUDIFVersion
) != udifVersion
) // current as of this writing
88 DiskImageRep::DiskImageRep(const char *path
)
94 void DiskImageRep::setup()
98 // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment
99 if (!readHeader(fd(), this->mHeader
))
100 UnixError::throwMe(errSecCSBadDiskImageFormat
);
102 mHeaderOffset
= fd().fileSize() - sizeof(UDIFFileHeader
);
103 size_t signatureOffset
= size_t(n2h(sigFields(this->mHeader
).fCodeSignatureOffset
));
104 size_t signatureLength
= size_t(n2h(sigFields(this->mHeader
).fCodeSignatureLength
));
105 sigFields(this->mHeader
).fCodeSignatureLength
= 0; // blind length (signature covers header)
106 if (signatureOffset
== 0) {
107 mEndOfDataOffset
= mHeaderOffset
;
108 sigFields(mHeader
).fCodeSignatureOffset
= h2n(mHeaderOffset
);
109 return; // unsigned, header prepared for possible signing
111 mEndOfDataOffset
= signatureOffset
;
114 // read the signature superblob
115 const size_t frameLength
= mHeaderOffset
- signatureOffset
; // room to following header
116 if (EmbeddedSignatureBlob
* blob
= EmbeddedSignatureBlob::readBlob(fd(), signatureOffset
, frameLength
)) {
117 if (blob
->length() != frameLength
|| frameLength
!= signatureLength
) {
119 MacOSError::throwMe(errSecCSBadDiskImageFormat
);
127 // The default binary identification of a SingleDiskRep is the (SHA-1) hash
128 // of the entire file itself.
130 CFDataRef
DiskImageRep::identification()
132 SHA1 hash
; // not security sensitive
133 hash(&mHeader
, sizeof(mHeader
));
136 return makeCFData(digest
, sizeof(digest
));
141 // Sniffer function for UDIF disk image files.
142 // This just looks for the trailing "header" and its magic number.
144 bool DiskImageRep::candidate(FileDesc
&fd
)
146 UDIFFileHeader header
;
147 return readHeader(fd
, header
) == true;
152 // Signing limit is the start of the (trailing) signature
154 size_t DiskImageRep::signingLimit()
156 return mEndOfDataOffset
;
159 void DiskImageRep::strictValidate(const CodeDirectory
* cd
, const ToleratedErrors
& tolerated
, SecCSFlags flags
)
161 DiskRep::strictValidate(cd
, tolerated
, flags
);
164 size_t cd_limit
= cd
->signingLimit();
165 size_t dr_limit
= signingLimit();
166 if (cd_limit
!= dr_limit
&& // must cover exactly the entire data
167 cd_limit
!= fd().fileSize()) // or, for legacy detached sigs, the entire file
168 MacOSError::throwMe(errSecCSSignatureInvalid
);
174 // Retrieve a component from the executable.
175 // Our mCache has mapped the entire file, so we just fish the contents out of
176 // the mapped area as needed.
178 CFDataRef
DiskImageRep::component(CodeDirectory::SpecialSlot slot
)
181 case cdRepSpecificSlot
:
182 return makeCFData(&mHeader
, sizeof(mHeader
));
184 return mSigningData
? mSigningData
->component(slot
) : NULL
;
190 // Provide a (vaguely) human readable characterization of this code
192 string
DiskImageRep::format()
197 void DiskImageRep::prepareForSigning(SigningContext
& context
)
199 // default to SHA256 unconditionally - we have no legacy issues to worry about
200 if (context
.digestAlgorithms().empty())
201 context
.setDigestAlgorithm(kSecCodeSignatureHashSHA256
);
206 // DiskImageRep::Writers
208 DiskRep::Writer
*DiskImageRep::writer()
210 return new Writer(this);
215 // Write a component.
217 void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
219 assert(slot
!= cdRepSpecificSlot
);
220 EmbeddedSignatureBlob::Maker::component(slot
, data
);
225 // Append the superblob we built to the cache file.
227 void DiskImageRep::Writer::flush()
229 delete mSigningData
; // ditch previous blob just in case
230 mSigningData
= Maker::make(); // assemble new signature SuperBlob
232 // write signature superblob
233 size_t location
= rep
->mEndOfDataOffset
;
236 fd().writeAll(*mSigningData
); // write signature
238 // now (re)write disk image header after it
239 UDIFFileHeader fullHeader
= rep
->mHeader
;
240 sigFields(fullHeader
).fCodeSignatureOffset
= h2n(location
);
241 sigFields(fullHeader
).fCodeSignatureLength
= h2n(mSigningData
->length());
242 fd().writeAll(&fullHeader
, sizeof(rep
->mHeader
));
247 // Discretionary manipulations
249 void DiskImageRep::Writer::addDiscretionary(CodeDirectory::Builder
&builder
)
254 } // end namespace CodeSigning
255 } // end namespace Security