]> git.saurik.com Git - apple/security.git/blame - Security/libsecurity_codesigning/lib/codedirectory.cpp
Security-57031.30.12.tar.gz
[apple/security.git] / Security / libsecurity_codesigning / lib / codedirectory.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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// codedirectory - format and operations for code signing "code directory" structures
26//
27#include "codedirectory.h"
28#include "csutilities.h"
29#include "CSCommonPriv.h"
30
31using namespace UnixPlusPlus;
32
33
34namespace Security {
35namespace CodeSigning {
36
37
38//
39// Highest understood special slot in this CodeDirectory.
40//
41CodeDirectory::SpecialSlot CodeDirectory::maxSpecialSlot() const
42{
43 SpecialSlot slot = this->nSpecialSlots;
44 if (slot > cdSlotMax)
45 slot = cdSlotMax;
46 return slot;
47}
48
49
50//
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.
55//
56const char *CodeDirectory::canonicalSlotName(SpecialSlot slot)
57{
58 switch (slot) {
59 case cdRequirementsSlot:
60 return kSecCS_REQUIREMENTSFILE;
61 case cdResourceDirSlot:
62 return kSecCS_RESOURCEDIRFILE;
63 case cdCodeDirectorySlot:
64 return kSecCS_CODEDIRECTORYFILE;
65 case cdSignatureSlot:
66 return kSecCS_SIGNATUREFILE;
67 case cdApplicationSlot:
68 return kSecCS_APPLICATIONFILE;
69 case cdEntitlementSlot:
70 return kSecCS_ENTITLEMENTFILE;
71 default:
72 return NULL;
73 }
74}
75
76
77//
78// Canonical attributes of SpecialSlots.
79//
80unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
81{
82 switch (slot) {
83 case cdRequirementsSlot:
84 return cdComponentIsBlob; // global
85 case cdCodeDirectorySlot:
86 return cdComponentPerArchitecture | cdComponentIsBlob;
87 case cdSignatureSlot:
88 return cdComponentPerArchitecture; // raw
89 case cdEntitlementSlot:
90 return cdComponentIsBlob; // global
91 case cdIdentificationSlot:
92 return cdComponentPerArchitecture; // raw
93 default:
94 return 0; // global, raw
95 }
96}
97
98
99//
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.
103//
104#if !defined(NDEBUG)
105const char * const CodeDirectory::debugSlotName[] = {
106 "codedirectory",
107 "info",
108 "requirements",
109 "resources",
110 "application",
111 "entitlement"
112};
113#endif //NDEBUG
114
115
116//
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).
124//
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.
129//
130void CodeDirectory::checkIntegrity() const
131{
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);
142
143 // now check interior offsets for validity
144 if (!stringAt(identOffset))
145 MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
420ff9d9
A
146 if (version >= supportsTeamID && teamIDOffset != 0 && !stringAt(teamIDOffset))
147 MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
427c49bc 148 if (!contains(hashOffset - int64_t(hashSize) * nSpecialSlots, hashSize * (int64_t(nSpecialSlots) + nCodeSlots)))
b1ab9ed8
A
149 MacOSError::throwMe(errSecCSSignatureFailed); // hash array out of blob range
150 if (const Scatter *scatter = this->scatterVector()) {
151 // the optional scatter vector is terminated with an element having (count == 0)
152 unsigned int pagesConsumed = 0;
427c49bc 153 for (;; scatter++) {
b1ab9ed8
A
154 if (!contains(scatter, sizeof(Scatter)))
155 MacOSError::throwMe(errSecCSSignatureFailed);
427c49bc
A
156 if (scatter->count == 0)
157 break;
b1ab9ed8 158 pagesConsumed += scatter->count;
b1ab9ed8 159 }
b1ab9ed8
A
160 if (!contains((*this)[pagesConsumed-1], hashSize)) // referenced too many main hash slots
161 MacOSError::throwMe(errSecCSSignatureFailed);
162 }
60c433a9
A
163
164 // check consistency between the page-coverage fields
165 if (pageSize) {
166 if (codeLimit == 0) // can't have paged signatures with no covered data
167 MacOSError::throwMe(errSecCSSignatureFailed);
168 size_t coveredPages = ((codeLimit-1) >> pageSize) + 1; // page slots required to cover codeLimit
169 if (coveredPages != nCodeSlots)
170 MacOSError::throwMe(errSecCSSignatureFailed);
171 } else {
172 if ((codeLimit > 0) != nCodeSlots) // must have one code slot, or none if no code
173 MacOSError::throwMe(errSecCSSignatureFailed);
174 }
b1ab9ed8
A
175}
176
177
178//
179// Validate a slot against data in memory.
180//
181bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
182{
183 secdebug("codedir", "%p validating slot %d", this, int(slot));
184 MakeHash<CodeDirectory> hasher(this);
185 Hashing::Byte digest[hasher->digestLength()];
186 generateHash(hasher, data, length, digest);
187 return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
188}
189
190
191//
192// Validate a slot against the contents of an open file. At most 'length' bytes
193// will be read from the file.
194//
195bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
196{
197 MakeHash<CodeDirectory> hasher(this);
198 Hashing::Byte digest[hasher->digestLength()];
199 generateHash(hasher, fd, digest, length);
200 return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
201}
202
203
204//
205// Check whether a particular slot is present.
206// Absense is indicated by either a zero hash, or by lying outside
207// the slot range.
208//
209bool CodeDirectory::slotIsPresent(Slot slot) const
210{
211 if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
212 const Hashing::Byte *digest = (*this)[slot];
213 for (unsigned n = 0; n < hashSize; n++)
214 if (digest[n])
215 return true; // non-zero digest => present
216 }
217 return false; // absent
218}
219
220
221//
222// Given a hash type code, create an appropriate subclass of DynamicHash
223// and return it. The caller owns the object and must delete it when done.
224// This function never returns NULL. It throws if the hashType is unsuupported,
225// or if there's an error creating the hasher.
226//
227DynamicHash *CodeDirectory::hashFor(HashAlgorithm hashType)
228{
229 CCDigestAlg alg;
230 switch (hashType) {
231 case kSecCodeSignatureHashSHA1: alg = kCCDigestSHA1; break;
232 case kSecCodeSignatureHashSHA256: alg = kCCDigestSHA256; break;
b1ab9ed8
A
233 default:
234 MacOSError::throwMe(errSecCSSignatureUnsupported);
235 }
236 return new CCHashInstance(alg);
237}
238
239
240//
241// Hash the next limit bytes of a file and return the digest.
242// If the file is shorter, hash as much as you can.
243// Limit==0 means unlimited (to end of file).
244// Return how many bytes were actually hashed.
245// Throw on any errors.
246//
247size_t CodeDirectory::generateHash(DynamicHash *hasher, FileDesc fd, Hashing::Byte *digest, size_t limit)
248{
249 size_t size = hashFileData(fd, hasher, limit);
250 hasher->finish(digest);
251 return size;
252}
253
254
255//
256// Ditto, but hash a memory buffer instead.
257//
258size_t CodeDirectory::generateHash(DynamicHash *hasher, const void *data, size_t length, Hashing::Byte *digest)
259{
260 hasher->update(data, length);
261 hasher->finish(digest);
262 return length;
263}
264
265
313fa17b
A
266//
267// Turn a hash of canonical type into a hex string
268//
269std::string CodeDirectory::hexHash(const unsigned char *hash) const
270{
271 size_t size = this->hashSize;
272 char result[2*size+1];
273 for (unsigned n = 0; n < size; n++)
274 sprintf(result+2*n, "%02.2x", hash[n]);
275 return result;
276}
277
278
279//
280// Generate a screening code string from a (complete) CodeDirectory.
281// This can be used to make a lightweight pre-screening code from (just) a CodeDirectory.
282//
283std::string CodeDirectory::screeningCode() const
284{
285 if (slotIsPresent(-cdInfoSlot)) // has Info.plist
286 return "I" + hexHash((*this)[-cdInfoSlot]); // use Info.plist hash
287 if (pageSize == 0) // good-enough proxy for "not a Mach-O file"
288 return "M" + hexHash((*this)[0]); // use hash of main executable
289 return "N"; // no suitable screening code
290}
291
292
b1ab9ed8
A
293} // CodeSigning
294} // Security
295
296
297//
298// Canonical text form for user-settable code directory flags.
299// Note: This table is actually exported from Security.framework.
300//
301const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[] = {
302 { "host", kSecCodeSignatureHost, true },
303 { "adhoc", kSecCodeSignatureAdhoc, false },
304 { "hard", kSecCodeSignatureForceHard, true },
305 { "kill", kSecCodeSignatureForceKill, true },
427c49bc
A
306 { "expires", kSecCodeSignatureForceExpiration, true },
307 { "restrict", kSecCodeSignatureRestrict, true },
308 { "enforcement", kSecCodeSignatureEnforcement, true },
420ff9d9 309 { "library-validation", kSecCodeSignatureLibraryValidation, true },
b1ab9ed8
A
310 { NULL }
311};