]> git.saurik.com Git - apple/security.git/blob - securityd/src/codesigdb.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / securityd / src / codesigdb.cpp
1 /*
2 * Copyright (c) 2003-2009,2012 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 secdebug("codesign", "start verify");
77
78 StLock<Mutex> _(process);
79 SecCodeRef code = process.currentGuest();
80 if (!code) {
81 secdebug("codesign", "no code base: fail");
82 return false;
83 }
84 if (SecRequirementRef requirement = verifier.requirement()) {
85 // If the ACL contains a code signature (requirement), we won't match against unsigned code at all.
86 // The legacy hash is ignored (it's for use by pre-Leopard systems).
87 secdebug("codesign", "CS requirement present; ignoring legacy hashes");
88 Server::active().longTermActivity();
89 switch (OSStatus rc = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement)) {
90 case noErr:
91 secdebug("codesign", "CS verify passed");
92 return true;
93 case errSecCSUnsigned:
94 secdebug("codesign", "CS verify against unsigned binary failed");
95 return false;
96 default:
97 secdebug("codesign", "CS verify failed OSStatus=%d", int32_t(rc));
98 return false;
99 }
100 }
101 switch (matchSignedClientToLegacyACL(process, code, verifier, context)) {
102 case noErr: // handled, allow access
103 return true;
104 case errSecCSUnsigned: { // unsigned client, complete legacy case
105 secdebug("codesign", "no CS requirement - using legacy hash");
106
107 /*
108 * We should stop supporting this case for binanaries
109 * built for modern OS/SDK, user should ad-hoc sign
110 * their binaries in that case.
111 *
112 * <rdar://problem/20546287>
113 */
114 Identity &clientIdentity = process;
115 try {
116 if (clientIdentity.getHash() == CssmData::wrap(verifier.legacyHash(), SHA1::digestLength)) {
117 secdebug("codesign", "direct match: pass");
118 return true;
119 }
120 } catch (...) {
121 secdebug("codesign", "exception getting client code hash: fail");
122 return false;
123 }
124 return false;
125 }
126 default: // client unsuitable, reject this match
127 return false;
128 }
129 }
130
131 //
132 // See if we can rewrite the ACL from legacy to Code Signing form without losing too much security.
133 // Returns true if the present validation should succeed (we probably rewrote the ACL).
134 // Returns false if the present validation shouldn't succeed based on what we did here (we may still
135 // have rewritten the ACL, in principle).
136 //
137 // Note that these checks add nontrivial overhead to ACL processing. We want to eventually phase
138 // this out, or at least make it an option that doesn't run all the time - perhaps an "extra legacy
139 // effort" per-client mode bit.
140 //
141 static string trim(string s, char delimiter)
142 {
143 string::size_type p = s.rfind(delimiter);
144 if (p != string::npos)
145 s = s.substr(p + 1);
146 return s;
147 }
148
149 static string trim(string s, char delimiter, string suffix)
150 {
151 s = trim(s, delimiter);
152 int preLength = s.length() - suffix.length();
153 if (preLength > 0 && s.substr(preLength) == suffix)
154 s = s.substr(0, preLength);
155 return s;
156 }
157
158 OSStatus CodeSignatures::matchSignedClientToLegacyACL(Process &process,
159 SecCodeRef code, const OSXVerifier &verifier, const AclValidationContext &context)
160 {
161 //
162 // Check whether we seem to be matching a legacy .Mac ACL against a member of the .Mac group
163 //
164 if (SecurityServerAcl::looksLikeLegacyDotMac(context)) {
165 Server::active().longTermActivity();
166 CFRef<SecRequirementRef> dotmac;
167 MacOSError::check(SecRequirementCreateGroup(CFSTR("dot-mac"), NULL, kSecCSDefaultFlags, &dotmac.aref()));
168 if (SecCodeCheckValidity(code, kSecCSDefaultFlags, dotmac) == noErr) {
169 secdebug("codesign", "client is a dot-mac application; update the ACL accordingly");
170
171 // create a suitable AclSubject (this is the above-the-API-line way)
172 CFRef<CFDataRef> reqdata;
173 MacOSError::check(SecRequirementCopyData(dotmac, kSecCSDefaultFlags, &reqdata.aref()));
174 RefPointer<CodeSignatureAclSubject> subject = new CodeSignatureAclSubject(NULL, "group://dot-mac");
175 subject->add((const BlobCore *)CFDataGetBytePtr(reqdata));
176
177 // add it to the ACL and pass the access check (we just quite literally did it above)
178 SecurityServerAcl::addToStandardACL(context, subject);
179 return noErr;
180 }
181 }
182
183 //
184 // Get best names for the ACL (legacy) subject and the (signed) client
185 //
186 CFRef<CFDictionaryRef> info;
187 MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
188 CFStringRef signingIdentity = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier));
189 if (!signingIdentity) // unsigned
190 return errSecCSUnsigned;
191
192 string bundleName; // client
193 if (CFDictionaryRef infoList = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList)))
194 if (CFStringRef name = CFStringRef(CFDictionaryGetValue(infoList, kCFBundleNameKey)))
195 bundleName = trim(cfString(name), '.');
196 if (bundleName.empty()) // fall back to signing identifier
197 bundleName = trim(cfString(signingIdentity), '.');
198
199 string aclName = trim(verifier.path(), '/', ".app"); // ACL
200
201 secdebug("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
202 bundleName.c_str(), aclName.c_str());
203
204 //
205 // Check whether we're matching a signed APPLE application against a legacy ACL by the same name
206 //
207 if (bundleName == aclName) {
208 const unsigned char reqData[] = { // "anchor apple", version 1 blob, embedded here
209 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10,
210 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03
211 };
212 CFRef<SecRequirementRef> apple;
213 MacOSError::check(SecRequirementCreateWithData(CFTempData(reqData, sizeof(reqData)),
214 kSecCSDefaultFlags, &apple.aref()));
215 Server::active().longTermActivity();
216 switch (OSStatus rc = SecCodeCheckValidity(code, kSecCSDefaultFlags, apple)) {
217 case noErr:
218 {
219 secdebug("codesign", "withstands strict scrutiny; quietly adding new ACL");
220 RefPointer<OSXCode> wrap = new OSXCodeWrap(code);
221 RefPointer<AclSubject> subject = new CodeSignatureAclSubject(OSXVerifier(wrap));
222 SecurityServerAcl::addToStandardACL(context, subject);
223 return noErr;
224 }
225 default:
226 secdebug("codesign", "validation fails with rc=%d, rejecting", int32_t(rc));
227 return rc;
228 }
229 secdebug("codesign", "does not withstand strict scrutiny; ask the user");
230 QueryCodeCheck query;
231 query.inferHints(process);
232 if (!query(verifier.path().c_str())) {
233 secdebug("codesign", "user declined equivalence: cancel the access");
234 CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
235 }
236 RefPointer<OSXCode> wrap = new OSXCodeWrap(code);
237 RefPointer<AclSubject> subject = new CodeSignatureAclSubject(OSXVerifier(wrap));
238 SecurityServerAcl::addToStandardACL(context, subject);
239 return noErr;
240 }
241
242 // not close enough to even ask - this can't match
243 return errSecCSReqFailed;
244 }