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
;