]> git.saurik.com Git - apple/security.git/blob - securityd/src/codesigdb.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / securityd / src / codesigdb.cpp
1 /*
2 * Copyright (c) 2003-2009,2012,2016 Apple 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 //
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
37 //
38 // Construct a CodeSignatures objects
39 //
40 CodeSignatures::CodeSignatures()
41 {
42 }
43
44 CodeSignatures::~CodeSignatures()
45 {
46 }
47
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 {
54 }
55
56
57 //
58 // Basic Identity objects
59 //
60 CodeSignatures::Identity::Identity() : mState(untried)
61 { }
62
63 CodeSignatures::Identity::~Identity()
64 { }
65
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
71 // in case we want to, well, creatively rewrite it for some reason.
72 //
73 bool CodeSignatures::verify(Process &process,
74 const OSXVerifier &verifier, const AclValidationContext &context)
75 {
76 secinfo("codesign", "start verify");
77
78 StLock<Mutex> _(process);
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).
82 secinfo("codesign", "CS requirement present; ignoring legacy hashes");
83 Server::active().longTermActivity();
84 switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, requirement)) {
85 case noErr:
86 secinfo("codesign", "CS verify passed");
87 return true;
88 case errSecCSUnsigned:
89 secinfo("codesign", "CS verify against unsigned binary failed");
90 return false;
91 default:
92 secinfo("codesign", "CS verify failed OSStatus=%d", int32_t(rc));
93 return false;
94 }
95 }
96 switch (matchSignedClientToLegacyACL(process, verifier, context)) {
97 case noErr: // handled, allow access
98 return true;
99 case errSecCSUnsigned: { // unsigned client, complete legacy case
100 secinfo("codesign", "no CS requirement - using legacy hash");
101
102 /*
103 * We should stop supporting this case for binaries
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)) {
112 secinfo("codesign", "direct match: pass");
113 return true;
114 }
115 } catch (...) {
116 secinfo("codesign", "exception getting client code hash: fail");
117 return false;
118 }
119 return false;
120 }
121 default: // client unsuitable, reject this match
122 return false;
123 }
124 }
125
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);
147 size_t preLength = s.length() - suffix.length();
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,
154 const OSXVerifier &verifier, const AclValidationContext &context)
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()));
163 if (process.checkValidity(kSecCSDefaultFlags, dotmac) == noErr) {
164 secinfo("codesign", "client is a dot-mac application; update the ACL accordingly");
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;
182 MacOSError::check(process.copySigningInfo(kSecCSSigningInformation, &info.aref()));
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
195
196 secinfo("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
197 bundleName.c_str(), aclName.c_str());
198
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();
211 switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, apple)) {
212 case noErr:
213 {
214 secinfo("codesign", "withstands strict scrutiny; quietly adding new ACL");
215 RefPointer<AclSubject> subject = process.copyAclSubject();
216 SecurityServerAcl::addToStandardACL(context, subject);
217 return noErr;
218 }
219 default:
220 secinfo("codesign", "validation fails with rc=%d, rejecting", int32_t(rc));
221 return rc;
222 }
223 }
224
225 // not close enough to even ask - this can't match
226 return errSecCSReqFailed;
227 }