1/* 2 * Copyright (c) 2006-2007 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// clientid - track and manage identity of securityd clients 25// 26#include "clientid.h" 27#include "server.h" 28#include <Security/SecCodePriv.h> 29 30 31// 32// Constructing a ClientIdentification doesn't do much. 33// We're waiting for setup(), which should be called by the child class's 34// constructor. 35// 36ClientIdentification::ClientIdentification() 37{ 38} 39 40 41// 42// Initialize the ClientIdentification. 43// This creates a process-level code object for the client. 44// 45void ClientIdentification::setup(pid_t pid) 46{ 47 StLock<Mutex> _(mLock); 48 if (OSStatus rc = SecCodeCreateWithPID(pid, kSecCSDefaultFlags, 49 &mClientProcess.aref())) 50 secdebug("clientid", "could not get code for process %d: OSStatus=%d", 51 pid, int32_t(rc)); 52 mGuests.erase(mGuests.begin(), mGuests.end()); 53} 54 55 56// 57// Return a SecCodeRef for the client process itself, regardless of 58// which guest within it is currently active. 59// Think twice before using this. 60// 61SecCodeRef ClientIdentification::processCode() const 62{ 63 return mClientProcess; 64} 65 66 67// 68// Return a SecCodeRef for the currently active guest within the client 69// process. 70// 71// We make a fair effort to cache client guest identities without over-growing 72// the cache. Note that there's currently no protocol for being notified of 73// a guest's death or disappearance (independent from the host process's death), 74// so we couldn't track guests live even if we tried. 75// 76// Note that this consults Server::connection for the currently serviced 77// Connection object, so this is not entirely a function of ClientIdentification state. 78// 79SecCodeRef ClientIdentification::currentGuest() const 80{ 81 if (GuestState *guest = current()) 82 return guest->code; 83 else 84 return mClientProcess; 85} 86 87ClientIdentification::GuestState *ClientIdentification::current() const 88{ 89 // if we have no client identification, we can't find a current guest either 90 if (!processCode()) 91 return NULL; 92 93 SecGuestRef guestRef = Server::connection().guestRef(); 94 95 // try to deliver an already-cached entry 96 { 97 StLock<Mutex> _(mLock); 98 GuestMap::iterator it = mGuests.find(guestRef); 99 if (it != mGuests.end()) 100 return &it->second; 101 } 102 103 // okay, make a new one (this may take a while) 104 CFRef<CFDictionaryRef> attributes = (guestRef == kSecNoGuest) 105 ? NULL 106 : makeCFDictionary(1, kSecGuestAttributeCanonical, CFTempNumber(guestRef).get()); 107 Server::active().longTermActivity(); 108 CFRef<SecCodeRef> code; 109 switch (OSStatus rc = SecCodeCopyGuestWithAttributes(processCode(), 110 attributes, kSecCSDefaultFlags, &code.aref())) { 111 case noErr: 112 break; 113 case errSecCSUnsigned: // not signed; clearly not a host 114 case errSecCSNotAHost: // signed but not marked as a (potential) host 115 code = mClientProcess; 116 break; 117 case errSecCSNoSuchCode: // potential host, but... 118 if (guestRef == kSecNoGuest) { // ... no guests (yet), so return the process 119 code = mClientProcess; 120 break; 121 } 122 // else fall through // ... the guest we expected to be there isn't 123 default: 124 MacOSError::throwMe(rc); 125 } 126 StLock<Mutex> _(mLock); 127 GuestState &slot = mGuests[guestRef]; 128 if (!slot.code) // if another thread didn't get here first... 129 slot.code = code; 130 return &slot; 131} 132 133 134// 135// Support for the legacy hash identification mechanism. 136// The legacy machinery deals exclusively in terms of processes. 137// It knows nothing about guests and their identities. 138// 139string ClientIdentification::getPath() const 140{ 141 assert(mClientProcess); 142 return codePath(currentGuest()); 143} 144 145const CssmData ClientIdentification::getHash() const 146{ 147 if (GuestState *guest = current()) { 148 if (!guest->gotHash) { 149 RefPointer<OSXCode> clientCode = new OSXCodeWrap(guest->code); 150 OSXVerifier::makeLegacyHash(clientCode, guest->legacyHash); 151 guest->gotHash = true; 152 } 153 return CssmData::wrap(guest->legacyHash, SHA1::digestLength); 154 } else 155 return CssmData(); 156} 157 158const bool ClientIdentification::checkAppleSigned() const 159{ 160 if (GuestState *guest = current()) { 161 if (!guest->checkedSignature) { 162 // This is the clownfish supported way to check for a Mac App Store or B&I signed build 163 CFStringRef requirementString = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])"); 164 SecRequirementRef secRequirementRef = NULL; 165 OSStatus status = SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &secRequirementRef); 166 if (status == errSecSuccess) { 167 OSStatus status = SecCodeCheckValidity(guest->code, kSecCSDefaultFlags, secRequirementRef); 168 if (status != errSecSuccess) { 169 secdebug("SecurityAgentXPCQuery", "code requirement check failed (%d)", (int32_t)status); 170 } else { 171 guest->appleSigned = true; 172 } 173 guest->checkedSignature = true; 174 } 175 CFRelease(secRequirementRef); 176 } 177 return guest->appleSigned; 178 } else 179 return false; 180} 181 182 183// 184// Bonus function: get the path out of a SecCodeRef 185// 186std::string codePath(SecStaticCodeRef code) 187{ 188 CFRef<CFURLRef> path; 189 MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref())); 190 return cfString(path); 191} 192 193 194// 195// Debug dump support 196// 197#if defined(DEBUGDUMP) 198 199static void dumpCode(SecCodeRef code) 200{ 201 CFRef<CFURLRef> path; 202 if (OSStatus rc = SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref())) 203 Debug::dump("unknown(rc=%d)", int32_t(rc)); 204 else 205 Debug::dump("%s", cfString(path).c_str()); 206} 207 208void ClientIdentification::dump() 209{ 210 Debug::dump(" client="); 211 dumpCode(mClientProcess); 212 for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) { 213 Debug::dump(" guest(0x%x)=", it->first); 214 dumpCode(it->second.code); 215 if (it->second.gotHash) 216 Debug::dump(" [got hash]"); 217 } 218} 219 220#endif //DEBUGDUMP 221