2 * Copyright (c) 2006-2010 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 // codedirectory - format and operations for code signing "code directory" structures
27 #include "codedirectory.h"
28 #include "csutilities.h"
29 #include "CSCommonPriv.h"
31 using namespace UnixPlusPlus
;
35 namespace CodeSigning
{
39 // Highest understood special slot in this CodeDirectory.
41 CodeDirectory::SpecialSlot
CodeDirectory::maxSpecialSlot() const
43 SpecialSlot slot
= this->nSpecialSlots
;
51 // Canonical filesystem names for select slot numbers.
52 // These are variously used for filenames, extended attribute names, etc.
53 // to get some consistency in naming. These are for storing signing-related
54 // data; they have no bearing on the actual hash slots in the CodeDirectory.
56 const char *CodeDirectory::canonicalSlotName(SpecialSlot slot
)
59 case cdRequirementsSlot
:
60 return kSecCS_REQUIREMENTSFILE
;
61 case cdResourceDirSlot
:
62 return kSecCS_RESOURCEDIRFILE
;
63 case cdCodeDirectorySlot
:
64 return kSecCS_CODEDIRECTORYFILE
;
66 return kSecCS_SIGNATUREFILE
;
67 case cdApplicationSlot
:
68 return kSecCS_APPLICATIONFILE
;
69 case cdEntitlementSlot
:
70 return kSecCS_ENTITLEMENTFILE
;
78 // Canonical attributes of SpecialSlots.
80 unsigned CodeDirectory::slotAttributes(SpecialSlot slot
)
83 case cdRequirementsSlot
:
84 return cdComponentIsBlob
; // global
85 case cdCodeDirectorySlot
:
86 return cdComponentPerArchitecture
| cdComponentIsBlob
;
88 return cdComponentPerArchitecture
; // raw
89 case cdEntitlementSlot
:
90 return cdComponentIsBlob
; // global
91 case cdIdentificationSlot
:
92 return cdComponentPerArchitecture
; // raw
94 return 0; // global, raw
100 // Symbolic names for code directory special slots.
101 // These are only used for debug output. They are not API-official.
102 // Needs to be coordinated with the cd*Slot enumeration in codedirectory.h.
105 const char * const CodeDirectory::debugSlotName
[] = {
117 // Check a CodeDirectory for basic integrity. This should ensure that the
118 // version is understood by our code, and that the internal structure
119 // (offsets etc.) is intact. In particular, it must make sure that no offsets
120 // point outside the CodeDirectory.
121 // Throws if the directory is corrupted or out of versioning bounds.
122 // Returns if the version is usable (perhaps with degraded features due to
123 // compatibility hacks).
125 // Note: There are some things we don't bother checking because they won't
126 // cause crashes, and will just be flagged as nonsense later. For example,
127 // a Bad Guy could overlap the identifier and hash fields, which is nonsense
128 // but not dangerous.
130 void CodeDirectory::checkIntegrity() const
132 // check version for support
133 if (!this->validateBlob())
134 MacOSError::throwMe(errSecCSSignatureInvalid
); // busted
135 if (version
> compatibilityLimit
)
136 MacOSError::throwMe(errSecCSSignatureUnsupported
); // too new - no clue
137 if (version
< earliestVersion
)
138 MacOSError::throwMe(errSecCSSignatureUnsupported
); // too old - can't support
139 if (version
> currentVersion
)
140 secdebug("codedir", "%p version 0x%x newer than current 0x%x",
141 this, uint32_t(version
), currentVersion
);
143 // now check interior offsets for validity
144 if (!stringAt(identOffset
))
145 MacOSError::throwMe(errSecCSSignatureFailed
); // identifier out of blob range
146 if (!contains(hashOffset
- uint64_t(hashSize
) * nSpecialSlots
, hashSize
* (uint64_t(nSpecialSlots
) + nCodeSlots
)))
147 MacOSError::throwMe(errSecCSSignatureFailed
); // hash array out of blob range
148 if (const Scatter
*scatter
= this->scatterVector()) {
149 // the optional scatter vector is terminated with an element having (count == 0)
150 unsigned int pagesConsumed
= 0;
151 while (scatter
->count
) {
152 if (!contains(scatter
, sizeof(Scatter
)))
153 MacOSError::throwMe(errSecCSSignatureFailed
);
154 pagesConsumed
+= scatter
->count
;
157 if (!contains(scatter
, sizeof(Scatter
))) // (even sentinel must be in range)
158 MacOSError::throwMe(errSecCSSignatureFailed
);
159 if (!contains((*this)[pagesConsumed
-1], hashSize
)) // referenced too many main hash slots
160 MacOSError::throwMe(errSecCSSignatureFailed
);
166 // Validate a slot against data in memory.
168 bool CodeDirectory::validateSlot(const void *data
, size_t length
, Slot slot
) const
170 secdebug("codedir", "%p validating slot %d", this, int(slot
));
171 MakeHash
<CodeDirectory
> hasher(this);
172 Hashing::Byte digest
[hasher
->digestLength()];
173 generateHash(hasher
, data
, length
, digest
);
174 return memcmp(digest
, (*this)[slot
], hasher
->digestLength()) == 0;
179 // Validate a slot against the contents of an open file. At most 'length' bytes
180 // will be read from the file.
182 bool CodeDirectory::validateSlot(FileDesc fd
, size_t length
, Slot slot
) const
184 MakeHash
<CodeDirectory
> hasher(this);
185 Hashing::Byte digest
[hasher
->digestLength()];
186 generateHash(hasher
, fd
, digest
, length
);
187 return memcmp(digest
, (*this)[slot
], hasher
->digestLength()) == 0;
192 // Check whether a particular slot is present.
193 // Absense is indicated by either a zero hash, or by lying outside
196 bool CodeDirectory::slotIsPresent(Slot slot
) const
198 if (slot
>= -Slot(nSpecialSlots
) && slot
< Slot(nCodeSlots
)) {
199 const Hashing::Byte
*digest
= (*this)[slot
];
200 for (unsigned n
= 0; n
< hashSize
; n
++)
202 return true; // non-zero digest => present
204 return false; // absent
209 // Given a hash type code, create an appropriate subclass of DynamicHash
210 // and return it. The caller owns the object and must delete it when done.
211 // This function never returns NULL. It throws if the hashType is unsuupported,
212 // or if there's an error creating the hasher.
214 DynamicHash
*CodeDirectory::hashFor(HashAlgorithm hashType
)
218 case kSecCodeSignatureHashSHA1
: alg
= kCCDigestSHA1
; break;
219 case kSecCodeSignatureHashSHA256
: alg
= kCCDigestSHA256
; break;
220 case kSecCodeSignatureHashPrestandardSkein160x256
: alg
= kCCDigestSkein160
; break;
221 case kSecCodeSignatureHashPrestandardSkein256x512
: alg
= kCCDigestSkein256
; break;
223 MacOSError::throwMe(errSecCSSignatureUnsupported
);
225 return new CCHashInstance(alg
);
230 // Hash the next limit bytes of a file and return the digest.
231 // If the file is shorter, hash as much as you can.
232 // Limit==0 means unlimited (to end of file).
233 // Return how many bytes were actually hashed.
234 // Throw on any errors.
236 size_t CodeDirectory::generateHash(DynamicHash
*hasher
, FileDesc fd
, Hashing::Byte
*digest
, size_t limit
)
238 size_t size
= hashFileData(fd
, hasher
, limit
);
239 hasher
->finish(digest
);
245 // Ditto, but hash a memory buffer instead.
247 size_t CodeDirectory::generateHash(DynamicHash
*hasher
, const void *data
, size_t length
, Hashing::Byte
*digest
)
249 hasher
->update(data
, length
);
250 hasher
->finish(digest
);
256 // Turn a hash of canonical type into a hex string
258 std::string
CodeDirectory::hexHash(const unsigned char *hash
) const
260 size_t size
= this->hashSize
;
261 char result
[2*size
+1];
262 for (unsigned n
= 0; n
< size
; n
++)
263 sprintf(result
+2*n
, "%02.2x", hash
[n
]);
269 // Generate a screening code string from a (complete) CodeDirectory.
270 // This can be used to make a lightweight pre-screening code from (just) a CodeDirectory.
272 std::string
CodeDirectory::screeningCode() const
274 if (slotIsPresent(-cdInfoSlot
)) // has Info.plist
275 return "I" + hexHash((*this)[-cdInfoSlot
]); // use Info.plist hash
276 if (pageSize
== 0) // good-enough proxy for "not a Mach-O file"
277 return "M" + hexHash((*this)[0]); // use hash of main executable
278 return "N"; // no suitable screening code
287 // Canonical text form for user-settable code directory flags.
288 // Note: This table is actually exported from Security.framework.
290 const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable
[] = {
291 { "host", kSecCodeSignatureHost
, true },
292 { "adhoc", kSecCodeSignatureAdhoc
, false },
293 { "hard", kSecCodeSignatureForceHard
, true },
294 { "kill", kSecCodeSignatureForceKill
, true },
295 { "expires", kSecCodeSignatureForceExpiration
, true },