2  * Copyright (c) 2006-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@ 
  24 // clientid - track and manage identity of securityd clients 
  28 #include <Security/SecCodePriv.h> 
  29 #include <Security/oidsattr.h> 
  30 #include <Security/SecCertificatePriv.h> 
  34 // Constructing a ClientIdentification doesn't do much. 
  35 // We're waiting for setup(), which should be called by the child class's 
  38 ClientIdentification::ClientIdentification() 
  39         : mGotPartitionId(false) 
  45 // Initialize the ClientIdentification. 
  46 // This creates a process-level code object for the client. 
  48 void ClientIdentification::setup(pid_t pid
) 
  50         StLock
<Mutex
> _(mLock
); 
  51         if (OSStatus rc 
= SecCodeCreateWithPID(pid
, kSecCSDefaultFlags
, 
  52                         &mClientProcess
.aref())) 
  53                 secdebug("clientid", "could not get code for process %d: OSStatus=%d", 
  55         mGuests
.erase(mGuests
.begin(), mGuests
.end()); 
  60 // Return a SecCodeRef for the client process itself, regardless of 
  61 // which guest within it is currently active. 
  62 // Think twice before using this. 
  64 SecCodeRef 
ClientIdentification::processCode() const 
  66         return mClientProcess
; 
  71 // Return a SecCodeRef for the currently active guest within the client 
  74 // We make a fair effort to cache client guest identities without over-growing 
  75 // the cache. Note that there's currently no protocol for being notified of 
  76 // a guest's death or disappearance (independent from the host process's death), 
  77 // so we couldn't track guests live even if we tried. 
  79 // Note that this consults Server::connection for the currently serviced 
  80 // Connection object, so this is not entirely a function of ClientIdentification state. 
  82 SecCodeRef 
ClientIdentification::currentGuest() const 
  84         if (GuestState 
*guest 
= current()) 
  87                 return mClientProcess
; 
  90 ClientIdentification::GuestState 
*ClientIdentification::current() const 
  92         // if we have no client identification, we can't find a current guest either 
  96         SecGuestRef guestRef 
= Server::connection().guestRef(); 
  98         // try to deliver an already-cached entry 
 100                 StLock
<Mutex
> _(mLock
); 
 101                 GuestMap::iterator it 
= mGuests
.find(guestRef
); 
 102                 if (it 
!= mGuests
.end()) 
 106         // okay, make a new one (this may take a while) 
 107         CFRef
<CFDictionaryRef
> attributes 
= (guestRef 
== kSecNoGuest
) 
 109                 : makeCFDictionary(1, kSecGuestAttributeCanonical
, CFTempNumber(guestRef
).get()); 
 110         Server::active().longTermActivity(); 
 111         CFRef
<SecCodeRef
> code
; 
 112         switch (OSStatus rc 
= SecCodeCopyGuestWithAttributes(processCode(), 
 113                 attributes
, kSecCSDefaultFlags
, &code
.aref())) { 
 116         case errSecCSUnsigned
:                  // not signed; clearly not a host 
 117         case errSecCSNotAHost
:                  // signed but not marked as a (potential) host 
 118                 code 
= mClientProcess
; 
 120         case errSecCSNoSuchCode
:                // potential host, but... 
 121                 if (guestRef 
== kSecNoGuest
) {  //  ... no guests (yet), so return the process 
 122                         code 
= mClientProcess
; 
 125                 // else fall through            //  ... the guest we expected to be there isn't 
 127                 MacOSError::throwMe(rc
); 
 129         StLock
<Mutex
> _(mLock
); 
 130         GuestState 
&slot 
= mGuests
[guestRef
]; 
 131         if (!slot
.code
) // if another thread didn't get here first... 
 138 // Return the partition id ascribed to this client. 
 139 // This is assigned to the whole client process - it is not per-guest. 
 141 std::string 
ClientIdentification::partitionId() const 
 143         if (!mGotPartitionId
) { 
 144                 mClientPartitionId 
= partitionIdForProcess(processCode()); 
 145                 mGotPartitionId 
= true; 
 147         return mClientPartitionId
; 
 151 static std::string 
hashString(CFDataRef data
) 
 153         CFIndex length 
= CFDataGetLength(data
); 
 154         const unsigned char *hash 
= CFDataGetBytePtr(data
); 
 155         char s
[2 * length 
+ 1]; 
 156         for (CFIndex n 
= 0; n 
< length
; n
++) 
 157                 sprintf(&s
[2*n
], "%2.2x", hash
[n
]); 
 162 std::string 
ClientIdentification::partitionIdForProcess(SecStaticCodeRef code
) 
 164         static CFStringRef 
const appleReq 
= CFSTR("anchor apple"); 
 165         static CFStringRef 
const masReq 
= CFSTR("anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]"); 
 166         static CFStringRef 
const developmentOrDevIDReq 
= CFSTR("anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13]" 
 168                                                                                                                    "anchor apple generic and certificate leaf[subject.CN] = \"Mac Developer:\"* and certificate 1[field.1.2.840.113635.100.6.2.1]"); 
 169         static SecRequirementRef apple
; 
 170         static SecRequirementRef mas
; 
 171         static SecRequirementRef developmentOrDevID
; 
 172         static dispatch_once_t onceToken
; 
 173         dispatch_once(&onceToken
, ^{ 
 174                 if (noErr 
!= SecRequirementCreateWithString(appleReq
, kSecCSDefaultFlags
, &apple
) 
 175                         || noErr 
!= SecRequirementCreateWithString(masReq
, kSecCSDefaultFlags
, &mas
) 
 176                         || noErr 
!= SecRequirementCreateWithString(developmentOrDevIDReq
, kSecCSDefaultFlags
, &developmentOrDevID
)) 
 181         switch (rc 
= SecStaticCodeCheckValidity(code
, kSecCSBasicValidateOnly
, apple
)) { 
 183         case errSecCSReqFailed
: 
 185         case errSecCSUnsigned
: 
 188                 MacOSError::throwMe(rc
); 
 190         CFRef
<CFDictionaryRef
> info
; 
 191         if (OSStatus irc 
= SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref())) 
 192                 MacOSError::throwMe(irc
); 
 195                 // for apple-signed code, take the team id if present, or make it canonical apple 
 196                 if (CFStringRef teamidRef 
= CFStringRef(CFDictionaryGetValue(info
, kSecCodeInfoTeamIdentifier
))) 
 197                         return "teamid:" + cfString(teamidRef
); 
 198                 else if (CFEqual(CFDictionaryGetValue(info
, kSecCodeInfoIdentifier
), CFSTR("com.apple.security"))) 
 199                         return "apple-tool:";   // take security(1) into a separate partition so it can't automatically peek into Apple's own 
 202         } else if (noErr 
== SecStaticCodeCheckValidity(code
, kSecCSBasicValidateOnly
, mas
)) { 
 203                 // for MAS-signed code, we take the embedded team identifier (verified by Apple) 
 204                 return "teamid:" + cfString(CFStringRef(CFDictionaryGetValue(info
, kSecCodeInfoTeamIdentifier
))); 
 205         } else if (noErr 
== SecStaticCodeCheckValidity(code
, kSecCSBasicValidateOnly
, developmentOrDevID
)) { 
 206                 // for developer-signed code, we take the team identifier from the signing certificate's OU field 
 207                 CFRef
<CFDictionaryRef
> info
; 
 208                 if (noErr 
!= (rc 
= SecCodeCopySigningInformation(code
, kSecCSSigningInformation
, &info
.aref()))) 
 209                         MacOSError::throwMe(rc
); 
 210                 CFArrayRef certChain 
= CFArrayRef(CFDictionaryGetValue(info
, kSecCodeInfoCertificates
)); 
 211                 SecCertificateRef signingCert 
= SecCertificateRef(CFArrayGetValueAtIndex(certChain
, 0)); 
 212                 CFRef
<CFStringRef
> ou
; 
 213                 SecCertificateCopySubjectComponent(signingCert
, &CSSMOID_OrganizationalUnitName
, &ou
.aref()); 
 214                 return "teamid:" + cfString(ou
); 
 216                 // cannot positively classify this code, but it's signed 
 217                 CFDataRef cdhashData 
= CFDataRef(CFDictionaryGetValue(info
, kSecCodeInfoUnique
)); 
 219                 return "cdhash:" + hashString(cdhashData
); 
 225 // Support for the legacy hash identification mechanism. 
 226 // The legacy machinery deals exclusively in terms of processes. 
 227 // It knows nothing about guests and their identities. 
 229 string 
ClientIdentification::getPath() const 
 231         assert(mClientProcess
); 
 232         return codePath(currentGuest()); 
 235 const CssmData 
ClientIdentification::getHash() const 
 237         if (GuestState 
*guest 
= current()) { 
 238                 if (!guest
->gotHash
) { 
 239                         RefPointer
<OSXCode
> clientCode 
= new OSXCodeWrap(guest
->code
); 
 240                         OSXVerifier::makeLegacyHash(clientCode
, guest
->legacyHash
); 
 241                         guest
->gotHash 
= true; 
 243                 return CssmData::wrap(guest
->legacyHash
, SHA1::digestLength
); 
 248 const bool ClientIdentification::checkAppleSigned() const 
 250         if (GuestState 
*guest 
= current()) { 
 251                 if (!guest
->checkedSignature
) { 
 252             // This is the clownfish supported way to check for a Mac App Store or B&I signed build 
 253             CFStringRef requirementString 
= CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])"); 
 254             SecRequirementRef  secRequirementRef 
= NULL
; 
 255             OSStatus status 
= SecRequirementCreateWithString(requirementString
, kSecCSDefaultFlags
, &secRequirementRef
); 
 256             if (status 
== errSecSuccess
) { 
 257                 OSStatus status 
= SecCodeCheckValidity(guest
->code
, kSecCSDefaultFlags
, secRequirementRef
); 
 258                 if (status 
!= errSecSuccess
) { 
 259                     secdebug("SecurityAgentXPCQuery", "code requirement check failed (%d)", (int32_t)status
); 
 261                     guest
->appleSigned 
= true; 
 263                 guest
->checkedSignature 
= true; 
 265             CFRelease(secRequirementRef
); 
 267                 return guest
->appleSigned
; 
 273 bool ClientIdentification::hasEntitlement(const char *name
) const 
 275         CFRef
<CFDictionaryRef
> info
; 
 276         MacOSError::check(SecCodeCopySigningInformation(processCode(), kSecCSDefaultFlags
, &info
.aref())); 
 277         CFCopyRef
<CFDictionaryRef
> entitlements 
= (CFDictionaryRef
)CFDictionaryGetValue(info
, kSecCodeInfoEntitlementsDict
); 
 278         if (entitlements 
&& entitlements
.is
<CFDictionaryRef
>()) { 
 279                 CFTypeRef value 
= CFDictionaryGetValue(entitlements
, CFTempString(name
)); 
 280                 if (value 
&& value 
!= kCFBooleanFalse
) 
 281                         return true;            // have entitlement, it's not <false/> - bypass partition construction 
 288 // Bonus function: get the path out of a SecCodeRef 
 290 std::string 
codePath(SecStaticCodeRef code
) 
 292         CFRef
<CFURLRef
> path
; 
 293         MacOSError::check(SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref())); 
 294         return cfString(path
); 
 299 // Debug dump support 
 301 #if defined(DEBUGDUMP) 
 303 static void dumpCode(SecCodeRef code
) 
 305         CFRef
<CFURLRef
> path
; 
 306         if (OSStatus rc 
= SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref())) 
 307                 Debug::dump("unknown(rc=%d)", int32_t(rc
)); 
 309                 Debug::dump("%s", cfString(path
).c_str()); 
 312 void ClientIdentification::dump() 
 314         Debug::dump(" client="); 
 315         dumpCode(mClientProcess
); 
 316         for (GuestMap::const_iterator it 
= mGuests
.begin(); it 
!= mGuests
.end(); ++it
) { 
 317                 Debug::dump(" guest(0x%x)=", it
->first
); 
 318                 dumpCode(it
->second
.code
); 
 319                 if (it
->second
.gotHash
) 
 320                         Debug::dump(" [got hash]");