]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/codedirectory.cpp
9423f8eff421792aefd47d5688536ae82595a1b5
[apple/libsecurity_codesigning.git] / lib / codedirectory.cpp
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
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
31 using namespace UnixPlusPlus;
32
33
34 namespace Security {
35 namespace CodeSigning {
36
37
38 //
39 // Canonical filesystem names for select slot numbers.
40 // These are variously used for filenames, extended attribute names, etc.
41 // to get some consistency in naming. These are for storing signing-related
42 // data; they have no bearing on the actual hash slots in the CodeDirectory.
43 //
44 const char *CodeDirectory::canonicalSlotName(SpecialSlot slot)
45 {
46 switch (slot) {
47 case cdRequirementsSlot:
48 return kSecCS_REQUIREMENTSFILE;
49 case cdResourceDirSlot:
50 return kSecCS_RESOURCEDIRFILE;
51 case cdCodeDirectorySlot:
52 return kSecCS_CODEDIRECTORYFILE;
53 case cdSignatureSlot:
54 return kSecCS_SIGNATUREFILE;
55 case cdApplicationSlot:
56 return kSecCS_APPLICATIONFILE;
57 case cdEntitlementSlot:
58 return kSecCS_ENTITLEMENTFILE;
59 default:
60 return NULL;
61 }
62 }
63
64
65 //
66 // Canonical attributes of SpecialSlots.
67 //
68 unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
69 {
70 switch (slot) {
71 case cdRequirementsSlot:
72 return cdComponentIsBlob; // global
73 case cdCodeDirectorySlot:
74 return cdComponentPerArchitecture | cdComponentIsBlob;
75 case cdSignatureSlot:
76 return cdComponentPerArchitecture; // raw
77 case cdEntitlementSlot:
78 return cdComponentIsBlob; // global
79 case cdIdentificationSlot:
80 return cdComponentPerArchitecture; // raw
81 default:
82 return 0; // global, raw
83 }
84 }
85
86
87 //
88 // Symbolic names for code directory special slots.
89 // These are only used for debug output. They are not API-official.
90 // Needs to be coordinated with the cd*Slot enumeration in codedirectory.h.
91 //
92 #if !defined(NDEBUG)
93 const char * const CodeDirectory::debugSlotName[] = {
94 "codedirectory",
95 "info",
96 "requirements",
97 "resources",
98 "application",
99 "entitlement"
100 };
101 #endif //NDEBUG
102
103
104 //
105 // Check a CodeDirectory for basic integrity. This should ensure that the
106 // version is understood by our code, and that the internal structure
107 // (offsets etc.) is intact. In particular, it must make sure that no offsets
108 // point outside the CodeDirectory.
109 // Throws if the directory is corrupted or out of versioning bounds.
110 // Returns if the version is usable (perhaps with degraded features due to
111 // compatibility hacks).
112 //
113 // Note: There are some things we don't bother checking because they won't
114 // cause crashes, and will just be flagged as nonsense later. For example,
115 // a Bad Guy could overlap the identifier and hash fields, which is nonsense
116 // but not dangerous.
117 //
118 void CodeDirectory::checkIntegrity() const
119 {
120 // check version for support
121 if (!this->validateBlob())
122 MacOSError::throwMe(errSecCSSignatureInvalid); // busted
123 if (version > compatibilityLimit)
124 MacOSError::throwMe(errSecCSSignatureUnsupported); // too new - no clue
125 if (version < earliestVersion)
126 MacOSError::throwMe(errSecCSSignatureUnsupported); // too old - can't support
127 if (version > currentVersion)
128 secdebug("codedir", "%p version 0x%x newer than current 0x%x",
129 this, uint32_t(version), currentVersion);
130
131 // now check interior offsets for validity
132 if (!stringAt(identOffset))
133 MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
134 if (!contains(hashOffset - hashSize * nSpecialSlots, hashSize * (nSpecialSlots + nCodeSlots)))
135 MacOSError::throwMe(errSecCSSignatureFailed); // hash array out of blob range
136 if (const Scatter *scatter = this->scatterVector()) {
137 // the optional scatter vector is terminated with an element having (count == 0)
138 unsigned int pagesConsumed = 0;
139 while (scatter->count) {
140 if (!contains(scatter, sizeof(Scatter)))
141 MacOSError::throwMe(errSecCSSignatureFailed);
142 pagesConsumed += scatter->count;
143 scatter++;
144 }
145 if (!contains(scatter, sizeof(Scatter))) // (even sentinel must be in range)
146 MacOSError::throwMe(errSecCSSignatureFailed);
147 if (!contains((*this)[pagesConsumed-1], hashSize)) // referenced too many main hash slots
148 MacOSError::throwMe(errSecCSSignatureFailed);
149 }
150 }
151
152
153 //
154 // Validate a slot against data in memory.
155 //
156 bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
157 {
158 secdebug("codedir", "%p validating slot %d", this, int(slot));
159 Hash::Byte digest[Hash::digestLength];
160 hash(data, length, digest);
161 return memcmp(digest, (*this)[slot], Hash::digestLength) == 0;
162 }
163
164
165 //
166 // Validate a slot against the contents of an open file. At most 'length' bytes
167 // will be read from the file.
168 //
169 bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
170 {
171 Hash::Digest digest;
172 hash(fd, digest, length);
173 return memcmp(digest, (*this)[slot], Hash::digestLength) == 0;
174 }
175
176
177 //
178 // Check whether a particular slot is present.
179 // Absense is indicated by either a zero hash, or by lying outside
180 // the slot range.
181 //
182 bool CodeDirectory::slotIsPresent(Slot slot) const
183 {
184 if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
185 const Hash::Byte *digest = (*this)[slot];
186 for (unsigned n = 0; n < Hash::digestLength; n++)
187 if (digest[n])
188 return true; // non-zero digest => present
189 }
190 return false; // absent
191 }
192
193
194 //
195 // Hash the next limit bytes of a file and return the digest.
196 // If the file is shorter, hash as much as you can.
197 // Limit==0 means unlimited (to end of file).
198 // Return how many bytes were actually hashed.
199 // Throw on any errors.
200 //
201 size_t CodeDirectory::hash(FileDesc fd, Hash::Byte *digest, size_t limit)
202 {
203 SHA1 hasher;
204 size_t size = hashFileData(fd, hasher, limit);
205 hasher.finish(digest);
206 return size;
207 }
208
209
210 //
211 // Ditto, but hash a memory buffer instead.
212 //
213 size_t CodeDirectory::hash(const void *data, size_t length, Hash::Byte *digest)
214 {
215 Hash hash;
216 hash(data, length);
217 hash.finish(digest);
218 return length;
219 }
220
221
222 } // CodeSigning
223 } // Security
224
225
226 //
227 // Canonical text form for user-settable code directory flags.
228 // Note: This table is actually exported from Security.framework.
229 //
230 const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[] = {
231 { "host", kSecCodeSignatureHost, true },
232 { "adhoc", kSecCodeSignatureAdhoc, false },
233 { "hard", kSecCodeSignatureForceHard, true },
234 { "kill", kSecCodeSignatureForceKill, true },
235 { "expires", kSecCodeSignatureForceExpiration, true },
236 { NULL }
237 };