]>
Commit | Line | Data |
---|---|---|
d8f41ccd | 1 | /* |
fa7225c8 A |
2 | * Copyright (c) 2003-2009,2012,2016 Apple Inc. All Rights Reserved. |
3 | * | |
d8f41ccd | 4 | * @APPLE_LICENSE_HEADER_START@ |
fa7225c8 | 5 | * |
d8f41ccd A |
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. | |
fa7225c8 | 12 | * |
d8f41ccd A |
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. | |
fa7225c8 | 20 | * |
d8f41ccd A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | ||
25 | // | |
26 | // codesigdb - code-hash equivalence database | |
27 | // | |
28 | #include "codesigdb.h" | |
29 | #include "process.h" | |
30 | #include "server.h" | |
31 | #include "agentquery.h" | |
32 | #include <security_utilities/memutils.h> | |
33 | #include <security_utilities/logging.h> | |
34 | #include <Security/SecRequirementPriv.h> | |
35 | ||
36 | ||
d8f41ccd A |
37 | // |
38 | // Construct a CodeSignatures objects | |
39 | // | |
5c19dc3a | 40 | CodeSignatures::CodeSignatures() |
d8f41ccd | 41 | { |
d8f41ccd A |
42 | } |
43 | ||
44 | CodeSignatures::~CodeSignatures() | |
45 | { | |
46 | } | |
47 | ||
d8f41ccd A |
48 | // |
49 | // (Re)open the equivalence database. | |
50 | // This is useful to switch to database in another volume. | |
51 | // | |
52 | void CodeSignatures::open(const char *path) | |
53 | { | |
d8f41ccd A |
54 | } |
55 | ||
56 | ||
57 | // | |
58 | // Basic Identity objects | |
59 | // | |
60 | CodeSignatures::Identity::Identity() : mState(untried) | |
61 | { } | |
62 | ||
63 | CodeSignatures::Identity::~Identity() | |
64 | { } | |
65 | ||
d8f41ccd A |
66 | // |
67 | // Verify signature matches. | |
68 | // This ends up getting called when a CodeSignatureAclSubject is validated. | |
69 | // The OSXVerifier describes what we require of the client code; the process represents | |
70 | // the requesting client; and the context gives us access to the ACL and its environment | |
fa7225c8 | 71 | // in case we want to, well, creatively rewrite it for some reason. |
d8f41ccd A |
72 | // |
73 | bool CodeSignatures::verify(Process &process, | |
74 | const OSXVerifier &verifier, const AclValidationContext &context) | |
75 | { | |
fa7225c8 | 76 | secinfo("codesign", "start verify"); |
d8f41ccd A |
77 | |
78 | StLock<Mutex> _(process); | |
d8f41ccd A |
79 | if (SecRequirementRef requirement = verifier.requirement()) { |
80 | // If the ACL contains a code signature (requirement), we won't match against unsigned code at all. | |
81 | // The legacy hash is ignored (it's for use by pre-Leopard systems). | |
fa7225c8 | 82 | secinfo("codesign", "CS requirement present; ignoring legacy hashes"); |
d8f41ccd | 83 | Server::active().longTermActivity(); |
fa7225c8 | 84 | switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, requirement)) { |
d8f41ccd | 85 | case noErr: |
fa7225c8 | 86 | secinfo("codesign", "CS verify passed"); |
d8f41ccd A |
87 | return true; |
88 | case errSecCSUnsigned: | |
fa7225c8 | 89 | secinfo("codesign", "CS verify against unsigned binary failed"); |
d8f41ccd A |
90 | return false; |
91 | default: | |
fa7225c8 | 92 | secinfo("codesign", "CS verify failed OSStatus=%d", int32_t(rc)); |
d8f41ccd A |
93 | return false; |
94 | } | |
95 | } | |
fa7225c8 | 96 | switch (matchSignedClientToLegacyACL(process, verifier, context)) { |
d8f41ccd A |
97 | case noErr: // handled, allow access |
98 | return true; | |
5c19dc3a | 99 | case errSecCSUnsigned: { // unsigned client, complete legacy case |
fa7225c8 | 100 | secinfo("codesign", "no CS requirement - using legacy hash"); |
5c19dc3a | 101 | |
fa7225c8 A |
102 | /* |
103 | * We should stop supporting this case for binaries | |
5c19dc3a A |
104 | * built for modern OS/SDK, user should ad-hoc sign |
105 | * their binaries in that case. | |
106 | * | |
107 | * <rdar://problem/20546287> | |
108 | */ | |
109 | Identity &clientIdentity = process; | |
110 | try { | |
111 | if (clientIdentity.getHash() == CssmData::wrap(verifier.legacyHash(), SHA1::digestLength)) { | |
fa7225c8 | 112 | secinfo("codesign", "direct match: pass"); |
5c19dc3a A |
113 | return true; |
114 | } | |
115 | } catch (...) { | |
fa7225c8 | 116 | secinfo("codesign", "exception getting client code hash: fail"); |
5c19dc3a A |
117 | return false; |
118 | } | |
119 | return false; | |
120 | } | |
d8f41ccd A |
121 | default: // client unsuitable, reject this match |
122 | return false; | |
123 | } | |
124 | } | |
125 | ||
d8f41ccd A |
126 | // |
127 | // See if we can rewrite the ACL from legacy to Code Signing form without losing too much security. | |
128 | // Returns true if the present validation should succeed (we probably rewrote the ACL). | |
129 | // Returns false if the present validation shouldn't succeed based on what we did here (we may still | |
130 | // have rewritten the ACL, in principle). | |
131 | // | |
132 | // Note that these checks add nontrivial overhead to ACL processing. We want to eventually phase | |
133 | // this out, or at least make it an option that doesn't run all the time - perhaps an "extra legacy | |
134 | // effort" per-client mode bit. | |
135 | // | |
136 | static string trim(string s, char delimiter) | |
137 | { | |
138 | string::size_type p = s.rfind(delimiter); | |
139 | if (p != string::npos) | |
140 | s = s.substr(p + 1); | |
141 | return s; | |
142 | } | |
143 | ||
144 | static string trim(string s, char delimiter, string suffix) | |
145 | { | |
146 | s = trim(s, delimiter); | |
fa7225c8 | 147 | size_t preLength = s.length() - suffix.length(); |
d8f41ccd A |
148 | if (preLength > 0 && s.substr(preLength) == suffix) |
149 | s = s.substr(0, preLength); | |
150 | return s; | |
151 | } | |
152 | ||
153 | OSStatus CodeSignatures::matchSignedClientToLegacyACL(Process &process, | |
fa7225c8 | 154 | const OSXVerifier &verifier, const AclValidationContext &context) |
d8f41ccd A |
155 | { |
156 | // | |
157 | // Check whether we seem to be matching a legacy .Mac ACL against a member of the .Mac group | |
158 | // | |
159 | if (SecurityServerAcl::looksLikeLegacyDotMac(context)) { | |
160 | Server::active().longTermActivity(); | |
161 | CFRef<SecRequirementRef> dotmac; | |
162 | MacOSError::check(SecRequirementCreateGroup(CFSTR("dot-mac"), NULL, kSecCSDefaultFlags, &dotmac.aref())); | |
fa7225c8 A |
163 | if (process.checkValidity(kSecCSDefaultFlags, dotmac) == noErr) { |
164 | secinfo("codesign", "client is a dot-mac application; update the ACL accordingly"); | |
d8f41ccd A |
165 | |
166 | // create a suitable AclSubject (this is the above-the-API-line way) | |
167 | CFRef<CFDataRef> reqdata; | |
168 | MacOSError::check(SecRequirementCopyData(dotmac, kSecCSDefaultFlags, &reqdata.aref())); | |
169 | RefPointer<CodeSignatureAclSubject> subject = new CodeSignatureAclSubject(NULL, "group://dot-mac"); | |
170 | subject->add((const BlobCore *)CFDataGetBytePtr(reqdata)); | |
171 | ||
172 | // add it to the ACL and pass the access check (we just quite literally did it above) | |
173 | SecurityServerAcl::addToStandardACL(context, subject); | |
174 | return noErr; | |
175 | } | |
176 | } | |
177 | ||
178 | // | |
179 | // Get best names for the ACL (legacy) subject and the (signed) client | |
180 | // | |
181 | CFRef<CFDictionaryRef> info; | |
fa7225c8 | 182 | MacOSError::check(process.copySigningInfo(kSecCSSigningInformation, &info.aref())); |
d8f41ccd A |
183 | CFStringRef signingIdentity = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier)); |
184 | if (!signingIdentity) // unsigned | |
185 | return errSecCSUnsigned; | |
186 | ||
187 | string bundleName; // client | |
188 | if (CFDictionaryRef infoList = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList))) | |
189 | if (CFStringRef name = CFStringRef(CFDictionaryGetValue(infoList, kCFBundleNameKey))) | |
190 | bundleName = trim(cfString(name), '.'); | |
191 | if (bundleName.empty()) // fall back to signing identifier | |
192 | bundleName = trim(cfString(signingIdentity), '.'); | |
193 | ||
194 | string aclName = trim(verifier.path(), '/', ".app"); // ACL | |
fa7225c8 A |
195 | |
196 | secinfo("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"", | |
d8f41ccd | 197 | bundleName.c_str(), aclName.c_str()); |
fa7225c8 | 198 | |
d8f41ccd A |
199 | // |
200 | // Check whether we're matching a signed APPLE application against a legacy ACL by the same name | |
201 | // | |
202 | if (bundleName == aclName) { | |
203 | const unsigned char reqData[] = { // "anchor apple", version 1 blob, embedded here | |
204 | 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, | |
205 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03 | |
206 | }; | |
207 | CFRef<SecRequirementRef> apple; | |
208 | MacOSError::check(SecRequirementCreateWithData(CFTempData(reqData, sizeof(reqData)), | |
209 | kSecCSDefaultFlags, &apple.aref())); | |
210 | Server::active().longTermActivity(); | |
fa7225c8 | 211 | switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, apple)) { |
d8f41ccd A |
212 | case noErr: |
213 | { | |
fa7225c8 A |
214 | secinfo("codesign", "withstands strict scrutiny; quietly adding new ACL"); |
215 | RefPointer<AclSubject> subject = process.copyAclSubject(); | |
d8f41ccd A |
216 | SecurityServerAcl::addToStandardACL(context, subject); |
217 | return noErr; | |
218 | } | |
219 | default: | |
fa7225c8 | 220 | secinfo("codesign", "validation fails with rc=%d, rejecting", int32_t(rc)); |
d8f41ccd A |
221 | return rc; |
222 | } | |
d8f41ccd A |
223 | } |
224 | ||
225 | // not close enough to even ask - this can't match | |
226 | return errSecCSReqFailed; | |
227 | } |