#include <Gestalt.h> #include <Math64.h> #include <DriverServices.h> #include <Multiprocessing.h> #include <UMemoryMgr.h> #include <LString.h> #include <KerberosSupport/Utilities.h> #include "AEClassicWorkaround.h" #include "CredentialsCacheInternal.h" #include "ContextDataCallStubs.h" #include "CCacheDataCallStubs.h" #include "CredentialsDataCallStubs.h" #include "ClassicProtocol.h" #include "UniqueGlobally.h" #include "ClassicStub.h" #include "CredentialsData.h" #include "CCacheData.h" #include "ContextData.h" #if CCache_ContainsSharedStaticData CCIUInt32 CCIClassicStub::sLastSeqNo = 0; bool CCIClassicStub::sDiffsHaveBeenInitialized = false; CCIUInt32 CCIClassicStub::sServerID = 0; #endif ProcessSerialNumber CCIClassicStub::sServerPSN; CCIClassicStub::CCIClassicStub () { } // Send message to server void CCIClassicStub::SendCCacheAE ( Ptr inEventData, Size inEventDataSize, CCIUInt32 inEventID, bool inWait, Handle& outReply, AEIdleUPP inIdleProc) const { OSErr err; AppleEvent event = {typeNull, NULL}; AppleEvent reply = {typeNull, NULL}; try { // Initialize the AE MakeAppleEvent (event); err = AEPutParamPtr (&event, ccClassic_Key_MessageID, typeMagnitude, &inEventID, sizeof (inEventID)); if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } // Put params in the AE err = AEPutParamPtr (&event, ccClassic_Key_Message, ccClassic_CCacheAEType, inEventData, inEventDataSize); if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } // Send AE SendAEToServer (event, reply, inWait, inIdleProc); // If we are receiving the reply right away, unmangle the reply if (inWait) { ExtractReplyMessage (reply, outReply); } // Cleanup if (event.dataHandle != NULL) AEDisposeDesc (&event); if (reply.dataHandle != NULL) AEDisposeDesc (&reply); } catch (...) { if (event.dataHandle != NULL) AEDisposeDesc (&event); if (reply.dataHandle != NULL) AEDisposeDesc (&reply); throw; } } // Unmangle server reply void CCIClassicStub::ExtractReplyMessage ( const AppleEvent& inReplyEvent, Handle& outReplyMessage) { DescType type; Size size; // Get the AE param from the AE reply OSErr err = AEGetParamPtr (&inReplyEvent, ccClassic_Key_Message, typeWildCard, &type, NULL, 0, &size); if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } // Create a new handle StHandleBlock replyData (size, false, true); // Failure to allocate if (replyData.Get () == NULL) { DebugThrow_ (CCIException (ccErrNoMem)); } // Lock the reply data StHandleLocker lock (replyData); // Copy the data into the new handle err = AEGetParamPtr (&inReplyEvent, ccClassic_Key_Message, typeWildCard, &type, *replyData, size, &size); if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } // Extract the data handle from the stack-based object and return it outReplyMessage = replyData.Get (); replyData.Release (); } // Make a new AE to send to the server void CCIClassicStub::MakeAppleEvent ( AppleEvent& outAppleEvent) const { AEAddressDesc serverAddress = {typeNull, NULL}; try { // Create an appropriate server address GetServerAddress (serverAddress); // Create the event OSErr err = AECreateAppleEvent (ccClassic_EventClass, ccClassic_EventID, &serverAddress, kAutoGenerateReturnID, kAnyTransactionID, &outAppleEvent); if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } if (serverAddress.dataHandle != NULL) AEDisposeDesc (&serverAddress); } catch (...) { if (serverAddress.dataHandle != NULL) AEDisposeDesc (&serverAddress); throw; } } // Make server address desc void CCIClassicStub::GetServerAddress ( AEAddressDesc& outServerAddress) const { // Always use a signature-based address OSType appCreator = ccClassic_YellowServerSignature; OSErr err = AECreateDesc (typeApplSignature, (Ptr) &appCreator, sizeof (appCreator), &outServerAddress); if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } } // Send an AE to the server void CCIClassicStub::SendAEToServer ( AppleEvent& inAppleEvent, AppleEvent& outReply, bool inWait, AEIdleUPP inIdleProc) const { // Launch the server first LaunchYellowServer (); // Let the server launch EventRecord event; WaitNextEvent (0, &event, 0, NULL); OSErr err; // Disable process switching to avoid eating suspend/resume // Use AESendWorkaround in utilities lib to work around some problems sending events from // Classic to yellow we see on Mac OS X 10.1.x if (inWait) { StDisableSwitching disableSwitching; err = AESendWorkaround (&inAppleEvent, &outReply, kAEWaitReply | kAECanInteract | kAECanSwitchLayer, kAEHighPriority, kAEDefaultTimeout, inIdleProc, NULL); } else { err = AESendWorkaround (&inAppleEvent, &outReply, kAEQueueReply | kAECanInteract | kAECanSwitchLayer, kAEHighPriority, kAEDefaultTimeout, inIdleProc, NULL); } if (err != noErr) { DebugThrow_ (CCIException (ccErrServerUnavailable)); } } // Verify if the cached PSN for the server is still valid (i.e. the server hasn't been // killed or restarted) bool CCIClassicStub::ServerPSNIsValid () { if ((sServerPSN.highLongOfPSN != 0) || (sServerPSN.lowLongOfPSN != 0)) { // We have a PSN that was valid at some point ProcessSerialNumber psn = sServerPSN; ProcessInfoRec pir; pir.processInfoLength = sizeof (pir); pir.processName = NULL; pir.processAppSpec = NULL; OSErr err = GetProcessInformation (&psn, &pir); if (err == noErr) { // The PSN is valid return true; } } return false; } // Launch the server void CCIClassicStub::LaunchYellowServer () { // First we check if the PSN we cached is valid. If it is, we // assume that the server is still there OSErr err; bool psnIsValid = ServerPSNIsValid (); // We need to make the AppleEvent now because we use it in two different ways below if (!psnIsValid) { // If the PSN is not valid, we need to launch it // Before we launch it, we have to find it, and to do that // we have to discover where the system is. We do that by looking // at where the Finder is, since there is no other way that I know // of to find the X volume from Classic static bool sHaveYellowVolume = false; static SInt16 sYellowVolumeRefNum = 0; if (!sHaveYellowVolume) { ProcessSerialNumber psn = {0, kNoProcess}; ProcessInfoRec pir; FSSpec appSpec; pir.processInfoLength = sizeof (pir); pir.processAppSpec = &appSpec; pir.processName = NULL; while (GetNextProcess (&psn) == noErr) { if (GetProcessInformation (&psn, &pir) == noErr) { if ((pir.processSignature == 'MACS') && (pir.processType == 'FNDR')) { sHaveYellowVolume = true; sYellowVolumeRefNum = appSpec.vRefNum; break; } } } } if (!sHaveYellowVolume) { CCIDebugThrow_ (CCIException (ccErrServerUnavailable)); } FSSpec serverSpec; err = FSMakeFSSpec (sYellowVolumeRefNum, fsRtDirID, "\p:System:Library:Frameworks:Kerberos.framework:Versions:A:Servers:CCacheClassicServer.app:Contents:MacOS:CCacheClassicServer", &serverSpec); if (err != noErr) { CCIDebugThrow_ (CCIException (ccErrServerUnavailable)); } // Note that if the server is already running, this will just return the existing PSN, which is exactly // what we want LaunchParamBlockRec launch; launch.launchBlockID = extendedBlock; launch.launchEPBLength = extendedBlockLen; launch.launchFileFlags = 0; launch.launchControlFlags = launchNoFileFlags | launchContinue | launchDontSwitch; launch.launchAppSpec = &serverSpec; launch.launchAppParameters = NULL; err = LaunchApplication (&launch); if (err == noErr) { sServerPSN = launch.launchProcessSN; } else { CCIDebugThrow_ (CCIException (ccErrServerUnavailable)); } while (!ServerPSNIsValid ()) { EventRecord event; WaitNextEvent (everyEvent, &event, 1, NULL); } } } // Reset the buffers void CCIClassicStub::Reset ( OSType inMessageType) { mSendBuffer.Reset (); mSendBuffer.Put (sServerID); mEventID = inMessageType; } // Send the current message to the server and handle the reply void CCIClassicStub::SendMessage () { // Get the initial diffs from the server before sending our message if (!sDiffsHaveBeenInitialized) { CCIClassicStub stub; stub.InitializeDiffs (); } // Lock the send buffer StHandleLocker lockMessage (mSendBuffer.GetHandle ()); // Clear the receive buffer mReceiveBuffer.DisposeHandle (); Handle reply; // Send the message SendCCacheAE (*mSendBuffer.GetHandle (), GetHandleSize (mSendBuffer.GetHandle ()), mEventID, true, reply, NULL); // Update hte receive buffer with the reply mReceiveBuffer.AdoptHandle (reply); mReceiveBuffer.Reset (); // Read the error codef rom the reply buffer CCIResult error; mReceiveBuffer.Get (error); if (error == noErr) { // Update the server ID mReceiveBuffer.Get (sServerID); // When we send a message to the classic server, the // response we get includes the changes which we need to apply // to the ccache in Classic to make sure it's in sync if (!ApplyCCacheDifferences ()) { CCIDebugThrow_ (CCIException (ccErrServerUnavailable)); } } else if (error == ccClassic_Err_YellowServerRestarted) { // This error means that someone restarted the yellow server // In that case, we have to reset our state completely ResetCCache (); // Ask the server to send us a completely new copy if the initial state // The caller should retry when this error occurs CCIClassicStub stub; stub.InitializeDiffs (); CCIDebugThrow_ (CCIException (error)); } else { CCIDebugThrow_ (CCIException (error)); } } #if CCI_DEBUG static std::string sSeqNos; #endif bool CCIClassicStub::ApplyCCacheDifferences () { #if CCI_DEBUG sSeqNos.erase (); #endif // Apply differences from the reply to the Classic copy of the cache CCIUInt32 blockType; bool result = true; // Read the block type from the reply mReceiveBuffer.Get (blockType); // If the next block is a diff, apply the diff while (blockType == ccClassic_DiffCookie) { result = ApplyOneCCacheDifference (); // Read the type of the next block mReceiveBuffer.Get (blockType); } #if CCI_DEBUG if (!result) { SignalPStr_ (LStr255 ("ApplyCCacheDifferences failed, ").Append (sSeqNos.c_str ())); } #endif // If the last diff was succesfully applied, that means that the // classic cache is up to date return result; } bool CCIClassicStub::ApplyOneCCacheDifference () { CCIUInt32 seqNo; // Get the sequence number of this diff mReceiveBuffer.Get (seqNo); // If we already have this difference, don't apply it again, but return success if (seqNo < sLastSeqNo) { #if CCI_DEBUG sSeqNos += LStr255 (static_cast <SInt16> (seqNo)).Append ('a').ConstTextPtr (); #endif return true; // If this diff is in future, refuse it (can't apply diffs out of order) } else if (seqNo != sLastSeqNo) { #if CCI_DEBUG sSeqNos += LStr255 (static_cast <SInt16> (seqNo)).Append ('f').ConstTextPtr (); #endif return false; } // Read the difference type CCIUInt32 differenceType; mReceiveBuffer.Get (differenceType); // Do the appropriate thing with the diff data based on the type switch (differenceType) { case ccClassic_Context_CreateCCache: { CCIUniqueID contextID; mReceiveBuffer.Get (contextID); std::string name; mReceiveBuffer.Get (name); CCIUInt32 version; mReceiveBuffer.Get (version); std::string principal; mReceiveBuffer.Get (principal); CCIContextDataCallStub context (contextID, ccapi_version_4); CCIUniqueID ccache = context.CreateCCache (name, version, principal); break; } case ccClassic_Context_CreateDefaultCCache: { CCIUniqueID contextID; mReceiveBuffer.Get (contextID); CCIUInt32 version; mReceiveBuffer.Get (version); std::string principal; mReceiveBuffer.Get (principal); CCIContextDataCallStub context (contextID, ccapi_version_4); CCIUniqueID ccache = context.CreateDefaultCCache (version, principal); break; } case ccClassic_Context_CreateNewCCache: { CCIUniqueID contextID; mReceiveBuffer.Get (contextID); CCIUInt32 version; mReceiveBuffer.Get (version); std::string principal; mReceiveBuffer.Get (principal); CCIContextDataCallStub context (contextID, ccapi_version_4); CCIUniqueID ccache = context.CreateNewCCache (version, principal); break; } case ccClassic_CCache_Destroy: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); ccache.Destroy (); break; } case ccClassic_CCache_SetDefault: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); ccache.SetDefault (); break; } case ccClassic_CCache_SetPrincipal: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); CCIUInt32 version; mReceiveBuffer.Get (version); std::string principal; mReceiveBuffer.Get (principal); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); ccache.SetPrincipal (version, principal); break; } case ccClassic_CCache_CompatSetPrincipal: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); CCIUInt32 version; mReceiveBuffer.Get (version); std::string principal; mReceiveBuffer.Get (principal); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); ccache.CompatSetPrincipal (version, principal); break; } case ccClassic_CCache_StoreConvertedCredentials: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); std::strstream flatCredentials; mReceiveBuffer.Get (flatCredentials); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); ccache.StoreFlattenedCredentials (flatCredentials); break; } case ccClassic_CCache_CompatStoreConvertedCredentials: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); std::strstream flatCredentials; mReceiveBuffer.Get (flatCredentials); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); ccache.CompatStoreFlattenedCredentials (flatCredentials); break; } case ccClassic_CCache_RemoveCredentials: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); CCIUniqueID credentialsID; mReceiveBuffer.Get (credentialsID); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); CCICredentialsDataCallStub credentials (credentialsID, ccapi_version_4); ccache.RemoveCredentials (credentials); break; } case ccClassic_CCache_Move: { CCIUniqueID ccacheID; mReceiveBuffer.Get (ccacheID); CCIUniqueID destinationID; mReceiveBuffer.Get (destinationID); CCICCacheDataCallStub ccache (ccacheID, ccapi_version_4); CCICCacheDataCallStub destination (destinationID, ccapi_version_4); ccache.Move (destination); break; } case ccClassic_CCache_SkipToID: { CCIUniqueID nextID; mReceiveBuffer.Get (nextID); CCIUniqueGlobally <CCICCacheData>::SetNextGloballyUniqueID (nextID); break; } case ccClassic_Credentials_SkipToID: { CCIUniqueID nextID; mReceiveBuffer.Get (nextID); CCIUniqueGlobally <CCICredentialsData>::SetNextGloballyUniqueID (nextID); break; } default: CCIAssert_ ("Unknown difference type in ApplyOneCCacheDifference."); } // Bump the sequence number IncrementSeqNo (); #if CCI_DEBUG sSeqNos += LStr255 (static_cast <SInt16> (seqNo)).Append ('t').ConstTextPtr (); #endif return true; } void CCIClassicStub::InitializeDiffs () { // Get the initial set of diffs from the server Reset (ccClassic_Context_FabricateInitialDiffs); Handle response; // Lock the send buffer StHandleLocker lockMessage (mSendBuffer.GetHandle ()); // Send the message SendCCacheAE (*mSendBuffer.GetHandle (), GetHandleSize (mSendBuffer.GetHandle ()), mEventID, true, response, NULL); // Setup the reply mReceiveBuffer.AdoptHandle (response); mReceiveBuffer.Reset (); // Read the error out of the reply CCIResult error; mReceiveBuffer.Get (error); if (error != ccNoError) { CCIDebugThrow_ (CCIException (error)); } // Update the server ID mReceiveBuffer.Get (sServerID); // Apply the initial diffs if (!ApplyCCacheDifferences ()) { CCIDebugThrow_ (CCIException (ccErrServerUnavailable)); } sDiffsHaveBeenInitialized = true; sLastSeqNo = 0; } // Reset the entire ccache to the initial state, in preparation for // getting new initial diffs void CCIClassicStub::ResetCCache () { CCIContextData* context = CCIContextDataInterface::GetGlobalContext (); // Get list of CCache IDs from the global contex std::vector <CCIObjectID> ccaches; context -> GetCCacheIDs (ccaches); std::vector <CCIObjectID>::iterator i; // Destroy all ccaches for (i = ccaches.begin (); i != ccaches.end (); i++) { CCICCacheDataInterface ccache (*i); ccache -> Destroy (); } // Reset globally unique IDs CCIUniqueGlobally <CCICCacheData>::SetNextGloballyUniqueID (0); CCIUniqueGlobally <CCICredentialsData>::SetNextGloballyUniqueID (0); // Reset classic stub data sServerID = 0; sDiffsHaveBeenInitialized = false; sLastSeqNo = 0; } // Send the yellow server a message telling it to produce diffs cc_int32 __CredentialsCacheInternalInitiateSyncWithYellowCache (void) { try { // Create a stub used to send this message CCIClassicStub stub; // Get initial diffs first if necessary if (!CCIClassicStub::sDiffsHaveBeenInitialized) { stub.Reset (ccClassic_Context_SyncWithYellowCache); stub.InitializeDiffs (); } stub.Reset (ccClassic_Context_SyncWithYellowCache); // Won't use this, there will be no response Handle dummyResponse; // Put the last seq no in the message data stub.mSendBuffer.Put (CCIClassicStub::sLastSeqNo); // Lock the send buffer StHandleLocker lockMessage (stub.mSendBuffer.GetHandle ()); // Send the message CCIClassicStub::SendCCacheAE (*stub.mSendBuffer.GetHandle (), GetHandleSize (stub.mSendBuffer.GetHandle ()), stub.mEventID, false, dummyResponse, NULL); // No reply return ccNoError; } catch (CCIException& e) { return e.Error (); } catch (...) { return ccErrServerUnavailable; } } // Handle a reply received from the yellow server in response to __...InitiateSync... cc_int32 __CredentialsCacheInternalCompleteSyncWithYellowCache ( const AppleEvent* inAppleEvent) { try { // Extract the reply from the AppleEvent Handle replyMessage; CCIClassicStub::ExtractReplyMessage (*inAppleEvent, replyMessage); // Create a new stub and tell it to use the reply CCIClassicStub stub; stub.mReceiveBuffer.AdoptHandle (replyMessage); stub.mReceiveBuffer.Reset (); // Extract the error from the reply CCIResult error; stub.mReceiveBuffer.Get (error); // This error means that someone restarted the yellow server // In that case, we have to reset our state completely // After we reset, just fall through and resync on the next call if (error == ccClassic_Err_YellowServerRestarted) { stub.ResetCCache (); error = ccErrServerUnavailable; } if (error != ccNoError) { CCIDebugThrow_ (CCIException (error)); } // Update the server ID stub.mReceiveBuffer.Get (CCIClassicStub::sServerID); // Apply the differences from the reply stub.ApplyCCacheDifferences (); return ccNoError; } catch (CCIException& e) { return e.Error (); } catch (...) { return ccErrServerUnavailable; } } // Sync with the yellow ccache (synchronously) // Essentially the same as __...InitializeSync... followed by __...CompleteSync... cc_int32 __CredentialsCacheInternalSyncWithYellowCache ( AEIdleUPP inIdleProc) { try { CCIClassicStub stub; // Initialize diffs if necessary if (!CCIClassicStub::sDiffsHaveBeenInitialized) { stub.Reset (ccClassic_Context_SyncWithYellowCache); stub.InitializeDiffs (); } stub.Reset (ccClassic_Context_SyncWithYellowCache); Handle response; // Put the last seq no in the message stub.mSendBuffer.Put (CCIClassicStub::sLastSeqNo); // Lock the message StHandleLocker lockMessage (stub.mSendBuffer.GetHandle ()); // Send the message CCIClassicStub::SendCCacheAE (*stub.mSendBuffer.GetHandle (), GetHandleSize (stub.mSendBuffer.GetHandle ()), stub.mEventID, true, response, inIdleProc); // Get the reply stub.mReceiveBuffer.AdoptHandle (response); stub.mReceiveBuffer.Reset (); // Extract error from reply CCIResult error; stub.mReceiveBuffer.Get (error); if (error == noErr) { // Update server ID stub.mReceiveBuffer.Get (CCIClassicStub::sServerID); // Apply diffs if (!stub.ApplyCCacheDifferences ()) { CCIDebugThrow_ (CCIException (ccErrServerUnavailable)); } } else if (error == ccClassic_Err_YellowServerRestarted) { // This error means that someone restarted the yellow server // In that case, we have to reset our state completely stub.ResetCCache (); CCIClassicStub stub; stub.InitializeDiffs (); CCIDebugThrow_ (CCIException (error)); } else { CCIDebugThrow_ (CCIException (error)); } return ccNoError; } catch (CCIException& e) { return e.Error (); } catch (...) { return ccErrServerUnavailable; } }