2 * Copyright (c) 2003-2009,2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // codesigdb - code-hash equivalence database
28 #include "codesigdb.h"
31 #include "agentquery.h"
32 #include <security_utilities/memutils.h>
33 #include <security_utilities/logging.h>
34 #include <Security/SecRequirementPriv.h>
38 // Construct a CodeSignatures objects
40 CodeSignatures::CodeSignatures()
44 CodeSignatures::~CodeSignatures()
49 // (Re)open the equivalence database.
50 // This is useful to switch to database in another volume.
52 void CodeSignatures::open(const char *path
)
58 // Basic Identity objects
60 CodeSignatures::Identity::Identity() : mState(untried
)
63 CodeSignatures::Identity::~Identity()
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.
73 bool CodeSignatures::verify(Process
&process
,
74 const OSXVerifier
&verifier
, const AclValidationContext
&context
)
76 secdebug("codesign", "start verify");
78 StLock
<Mutex
> _(process
);
79 SecCodeRef code
= process
.currentGuest();
81 secdebug("codesign", "no code base: fail");
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
)) {
91 secdebug("codesign", "CS verify passed");
93 case errSecCSUnsigned
:
94 secdebug("codesign", "CS verify against unsigned binary failed");
97 secdebug("codesign", "CS verify failed OSStatus=%d", int32_t(rc
));
101 switch (matchSignedClientToLegacyACL(process
, code
, verifier
, context
)) {
102 case noErr
: // handled, allow access
104 case errSecCSUnsigned
: { // unsigned client, complete legacy case
105 secdebug("codesign", "no CS requirement - using legacy hash");
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.
112 * <rdar://problem/20546287>
114 Identity
&clientIdentity
= process
;
116 if (clientIdentity
.getHash() == CssmData::wrap(verifier
.legacyHash(), SHA1::digestLength
)) {
117 secdebug("codesign", "direct match: pass");
121 secdebug("codesign", "exception getting client code hash: fail");
126 default: // client unsuitable, reject this match
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).
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.
141 static string
trim(string s
, char delimiter
)
143 string::size_type p
= s
.rfind(delimiter
);
144 if (p
!= string::npos
)
149 static string
trim(string s
, char delimiter
, string suffix
)
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
);
158 OSStatus
CodeSignatures::matchSignedClientToLegacyACL(Process
&process
,
159 SecCodeRef code
, const OSXVerifier
&verifier
, const AclValidationContext
&context
)
162 // Check whether we seem to be matching a legacy .Mac ACL against a member of the .Mac group
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");
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
));
177 // add it to the ACL and pass the access check (we just quite literally did it above)
178 SecurityServerAcl::addToStandardACL(context
, subject
);
184 // Get best names for the ACL (legacy) subject and the (signed) client
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
;
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
), '.');
199 string aclName
= trim(verifier
.path(), '/', ".app"); // ACL
201 secdebug("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
202 bundleName
.c_str(), aclName
.c_str());
205 // Check whether we're matching a signed APPLE application against a legacy ACL by the same name
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
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
)) {
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
);
226 secdebug("codesign", "validation fails with rc=%d, rejecting", int32_t(rc
));
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
);
236 RefPointer
<OSXCode
> wrap
= new OSXCodeWrap(code
);
237 RefPointer
<AclSubject
> subject
= new CodeSignatureAclSubject(OSXVerifier(wrap
));
238 SecurityServerAcl::addToStandardACL(context
, subject
);
242 // not close enough to even ask - this can't match
243 return errSecCSReqFailed
;