2 * Copyright (c) 2003-2009,2012,2016 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 secinfo("codesign", "start verify");
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
)) {
86 secinfo("codesign", "CS verify passed");
88 case errSecCSUnsigned
:
89 secinfo("codesign", "CS verify against unsigned binary failed");
92 secinfo("codesign", "CS verify failed OSStatus=%d", int32_t(rc
));
96 switch (matchSignedClientToLegacyACL(process
, verifier
, context
)) {
97 case noErr
: // handled, allow access
99 case errSecCSUnsigned
: { // unsigned client, complete legacy case
100 secinfo("codesign", "no CS requirement - using legacy hash");
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.
107 * <rdar://problem/20546287>
109 Identity
&clientIdentity
= process
;
111 if (clientIdentity
.getHash() == CssmData::wrap(verifier
.legacyHash(), SHA1::digestLength
)) {
112 secinfo("codesign", "direct match: pass");
116 secinfo("codesign", "exception getting client code hash: fail");
121 default: // client unsuitable, reject this match
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).
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.
136 static string
trim(string s
, char delimiter
)
138 string::size_type p
= s
.rfind(delimiter
);
139 if (p
!= string::npos
)
144 static string
trim(string s
, char delimiter
, string suffix
)
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
);
153 OSStatus
CodeSignatures::matchSignedClientToLegacyACL(Process
&process
,
154 const OSXVerifier
&verifier
, const AclValidationContext
&context
)
157 // Check whether we seem to be matching a legacy .Mac ACL against a member of the .Mac group
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");
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
));
172 // add it to the ACL and pass the access check (we just quite literally did it above)
173 SecurityServerAcl::addToStandardACL(context
, subject
);
179 // Get best names for the ACL (legacy) subject and the (signed) client
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
;
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
), '.');
194 string aclName
= trim(verifier
.path(), '/', ".app"); // ACL
196 secinfo("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
197 bundleName
.c_str(), aclName
.c_str());
200 // Check whether we're matching a signed APPLE application against a legacy ACL by the same name
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
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
)) {
214 secinfo("codesign", "withstands strict scrutiny; quietly adding new ACL");
215 RefPointer
<AclSubject
> subject
= process
.copyAclSubject();
216 SecurityServerAcl::addToStandardACL(context
, subject
);
220 secinfo("codesign", "validation fails with rc=%d, rejecting", int32_t(rc
));
225 // not close enough to even ask - this can't match
226 return errSecCSReqFailed
;