// =========================================================================== // UAMKeychain.c © 1999-2000 Microsoft Corp. All rights reserved. // =========================================================================== // Keychain support routines for the MS UAM. // // =========================================================================== #include "UAMDebug.h" #include "UAMUtils.h" #include "UAMMain.h" #include "UAMDialogs.h" #include "UAMNetwork.h" #include "UAMKeychain.h" Boolean gKeychainAvailable = false; Boolean gAllowedToSavePswd = false; extern Str32 gServerName; extern Str32 gUserName; extern Str32 gZoneName; extern OTAddress* gServerAddress; extern long gSupportedUAMs; // --------------------------------------------------------------------------- // ¥ UAM_KCInitialize() // --------------------------------------------------------------------------- // See if the keychain is supported on this machine as well as perform some // initialization for our keychain support. void UAM_KCInitialize(UAMArgs* inUAMArgs) { UInt32 theVersion; SInt32 theResponse; OSErr theError; gKeychainAvailable = false; gAllowedToSavePswd = false; // //Do a check here to make sure that we're running on a //system version that we support. // theError = Gestalt(gestaltSystemVersion, &theResponse); if (theError != noErr) { // //If there's an error calling Gestalt(), we're in real trouble. // return; } if (LoWord(theResponse) < 0x0900) { // //We don't support any OS older than MacOS 9.0. This is //because the old Keychain (v1.01) on older systems is //not stable enough and crashes often. // return; } // //Let's see if the Apple KeyChain manager is available and //remember it. // gKeychainAvailable = KeychainManagerAvailable(); if (gKeychainAvailable) { // //Get the version of the keychain manager. OS9 uses //v2.0 and is the first shipping version. However, v1.0 //was available to pre-OS9 macs. // KCGetKeychainManagerVersion(&theVersion); // //The version is kept in the hi-word of the return. // if (HiWord(theVersion) >= 0x0200) { // //Just in case someone else turned it off, we make sure //interaction is enabled. This call is only supported in //Keychain 2.0 or later. // KCSetInteractionAllowed(true); } } // //For debug, print out the availability. // DbgPrint_((DBGBUFF, "Keychain Manager available = %d", gKeychainAvailable)); // //BIT_2 of the srvr flags tells us if the user is allowed //to save their passwords in the keychain. // gAllowedToSavePswd = ((inUAMArgs->Opt.open.srvrInfo->fFlags & BIT_2) == 0); } // --------------------------------------------------------------------------- // ¥ UAM_KCAvailable() // --------------------------------------------------------------------------- // Returns TRUE if the keychain is available on this machine as well as if // the AFP server allows clients to save passwords on the workstation. Boolean UAM_KCAvailable(void) { return(((gKeychainAvailable) && (gAllowedToSavePswd)) ? true : false); } // --------------------------------------------------------------------------- // ¥ UAM_KCDeleteItem() // --------------------------------------------------------------------------- // Deletes a keychain item that pertains to this username & server. OSStatus UAM_KCDeleteItem( StringPtr inUserName, Str255 inServerName ) { OSStatus theStatus; KCItemRef theItem = NULL; theStatus = UAM_KCFindAppleSharePassword( inUserName, NULL, inServerName, &theItem ); if (theStatus != noErr) { // //We couldn't find an existing keychain item matching //the criteria. Bail. // return(theStatus); } Assert_(theItem != NULL); if (theItem != NULL) { // //We found the item, remove it from the keychain. // theStatus = KCDeleteItem(theItem); // //The keychain manager allocated this memory, we need to //free it. For free builds, we don't do anything if this //fails since there's nothing we or the user can do about it. // if (KCReleaseItem(&theItem) != noErr) { DbgPrint_((DBGBUFF, "KCReleaseItem() failed")); } } return(theStatus); } // --------------------------------------------------------------------------- // ¥ UAM_KCSavePassword() // --------------------------------------------------------------------------- OSStatus UAM_KCSavePassword( StringPtr inUserName, StringPtr inPassword, Str255 inServerName ) { OSStatus theStatus; OSType theTypeData; KCAttribute theAttribute; KCItemRef theItem = NULL; Boolean theIconFlag = true; PUAM_AFPXVolMountInfo theUAMInfo = NULL; Assert_(UAM_KCAvailable() == true); // //Search for an item in the keychain that already matches //what we are about to add. Note we have to do this because //of a bug in the keychain manager that prevents it from //doing it for us. // theStatus = UAM_KCFindAppleSharePassword( inUserName, NULL, inServerName, NULL ); if (theStatus == noErr) { // //If we get here, then that means there is already //an item in the keychain for this server and account. // return(errKCDuplicateItem); } // //Call our function that builds the AFPXVolMountInfo //structure that we pass to the keychain routine. // theStatus = UAM_BuildAFPXVolMountInfo( inUserName, inPassword, inServerName, (gSupportedUAMs & kMSUAM_V2_Supported) ? PSTR_EncryptedLogin2_0 : PSTR_EncryptedLogin1_0, &theUAMInfo ); if (theStatus != noErr) { // //If we failed here, it's bad news. This means we //don't have enough memory to allocate a small //buffer. // return(theStatus); } // //Have the keychain store our key information for this //server. // theStatus = KCAddAppleSharePassword( NULL, (PSTR_LENGTH(gZoneName)) ? gZoneName : NULL, inServerName, NULL, inUserName, sizeof(UAM_AFPXVolMountInfo), theUAMInfo, &theItem ); if (theStatus != noErr) { // //Check for cancel action by the user and report error. // if (theStatus != userCanceledErr) { DbgPrint_((DBGBUFF, "KCAddAppleSharePassword() failed (%d)", (int)theStatus)); } } else { do { // //Set the title of this keychain item so folks know //what it's for. // theAttribute.tag = kDescriptionKCItemAttr; theAttribute.length = PSTR_LENGTH(UAM_KC_DESCRIPTION); theAttribute.data = (void*)&UAM_KC_DESCRIPTION[1]; theStatus = KCSetAttribute(theItem, &theAttribute); if (theStatus != noErr) { DbgPrint_((DBGBUFF, "KCSetAttribute(kLabelKCItemAttr) failed (%d)", (int)theStatus)); break; } // //Tell the keychain to use our MS UAM icon when displaying //the keys to the user. // // //Set the creator of the MS UAM. // theTypeData = UAM_CREATOR; theAttribute.tag = kCreatorKCItemAttr; theAttribute.length = sizeof(OSType); theAttribute.data = &theTypeData; theStatus = KCSetAttribute(theItem, &theAttribute); if (theStatus != noErr) { DbgPrint_((DBGBUFF, "KCSetAttribute(kCreatorKCItemAttr) failed (%d)", (int)theStatus)); break; } // //Set the type to our code type (uams) // theTypeData = UAM_TYPE; theAttribute.tag = kTypeKCItemAttr; theAttribute.length = sizeof(OSType); theAttribute.data = &theTypeData; theStatus = KCSetAttribute(theItem, &theAttribute); if (theStatus != noErr) { DbgPrint_((DBGBUFF, "KCSetAttribute(kTypeKCItemAttr) failed (%d)", (int)theStatus)); break; } // //Lastly, tell the keychain manager we have a custom icon. // theAttribute.tag = kCustomIconKCItemAttr; theAttribute.length = sizeof(Boolean); theAttribute.data = &theIconFlag; theStatus = KCSetAttribute(theItem, &theAttribute); if (theStatus != noErr) { DbgPrint_((DBGBUFF, "KCSetAttribute(kCustomIconKCItemAttr) failed (%d)", (int)theStatus)); break; } }while(false); // //Only do the update if everthing above passed. Otherwise //we'll loose the error code. What to do, what to do... // if (theStatus == noErr) { // //After setting the attributes on the item, we need to tell //the keychain to save our changes. // theStatus = KCUpdateItem(theItem); if (theStatus != noErr) { DbgPrint_((DBGBUFF, "KCUpdateItem() failed (%d);g", (int)theStatus)); } } // //The keychain manager allocated this memory, we need to //free it. For free builds, we don't do anything if this //fails since there's nothing we or the user can do about it. // if (KCReleaseItem(&theItem) != noErr) { DbgPrint_((DBGBUFF, "KCReleaseItem() failed")); } } // //Free up our mount volume info structure. // if (theUAMInfo != NULL) { DisposePtr((Ptr)theUAMInfo); } return(theStatus); } // --------------------------------------------------------------------------- // ¥ UAM_KCFindAppleSharePassword() // --------------------------------------------------------------------------- // Look for a password associated with this server and account in the // keychain. OSStatus UAM_KCFindAppleSharePassword( StringPtr inUserName, StringPtr inPassword, StringPtr inServerName, KCItemRef* outItemRef ) { OSStatus theStatus = noErr; UInt32 theActualLen = 0; UInt32 theBuffSize = sizeof(UAM_AFPXVolMountInfo); PUAM_AFPXVolMountInfo theUamInfo = NULL; Assert_(UAM_KCAvailable() == true); do { theUamInfo = (PUAM_AFPXVolMountInfo)NewPtrClear(theBuffSize); if (theUamInfo == NULL) { theStatus = memFullErr; break; } theStatus = KCFindAppleSharePassword( NULL, NULL, inServerName, NULL, inUserName, theBuffSize, theUamInfo, &theActualLen, outItemRef ); // //If the buffer we supplied is too small, then reallocate the //buffer according to what is actually needed. NOTE: We will //only need to reallocate when looking at keychains that we //did not create ourselves. // if (theStatus == errKCBufferTooSmall) { DisposePtr((Ptr)theUamInfo); theUamInfo = NULL; theBuffSize = theActualLen; DbgPrint_((DBGBUFF, "Reallocating for %d bytes", (int)theActualLen)); continue; } break; }while(TRUE); if (theStatus == noErr) { // //Initialize expecting failure. For lack of anything //better we return param error. // theStatus = paramErr; // //First make sure we have a proper mount structure. // if ( (theUamInfo->media == AppleShareMediaType) && (theUamInfo->userPasswordOffset != 0) ) { // //Copy the password into a temp buffer and make sure it's //not zero length. But, only if inPassword is not null. // if (inPassword != NULL) { // //01.16.02: Pass maximum length to string copy routine. // StringPtr thePassword = (StringPtr)(((UInt32)theUamInfo) + theUamInfo->userPasswordOffset); UAM_PStrCopy(thePassword, inPassword, UAM_MAX_LMv2_PASSWORD); } theStatus = noErr; } } else if (theStatus != errKCItemNotFound) { // //For debugging only, we print out the error code. // DbgPrint_((DBGBUFF, "KCFindAppleSharePassword() failed (%d)", (int)theStatus)); } // //We don't need this buffer anymore, free it up. // if (theUamInfo != NULL) { DisposePtr((Ptr)theUamInfo); } return(theStatus); } // --------------------------------------------------------------------------- // ¥ UAM_BuildAFPXVolMountInfo() // --------------------------------------------------------------------------- // Builds the AFPXVolMountInfo structure that we need to send to the keychain. OSStatus UAM_BuildAFPXVolMountInfo( StringPtr inUserName, StringPtr inPassword, Str255 inServerName, const Str32 inUAMString, PUAM_AFPXVolMountInfo* outVolInfo ) { PUAM_AFPXVolMountInfo uamInfo = NULL; Size uamInfoSize = 0; *outVolInfo = NULL; uamInfoSize = sizeof(UAM_AFPXVolMountInfo); uamInfo = (PUAM_AFPXVolMountInfo)NewPtrClear(uamInfoSize); if (uamInfo != NULL) { uamInfo->length = uamInfoSize; uamInfo->media = AppleShareMediaType; uamInfo->flags = 0; // //We're not going to pass any alternate address info. We'll //let the AS Client do that for us. // uamInfo->extendedFlags = 0; uamInfo->alternateAddressOffset = 0; // //NBP and UAM Type stuff. Note we use our unique UAM indetifier //for the uam type. // uamInfo->nbpInterval = 10; uamInfo->nbpCount = 10; uamInfo->uamType = UAM_TYPE_CODE; // //Now setup all the offsets for the parameters. Yuck! // // //We don't always get a zone name from the client, we get //nil if we're using IP. // if (PSTR_LENGTH(gZoneName) > 0) { uamInfo->zoneNameOffset = uamx_member_offset(zoneName); UAM_PStrCopy(gZoneName, uamInfo->zoneName, sizeof(uamInfo->zoneName)); } else { uamInfo->zoneNameOffset = 0; } uamInfo->volNameOffset = 0; uamInfo->serverNameOffset = uamx_member_offset(serverName); uamInfo->userNameOffset = uamx_member_offset(userName); uamInfo->userPasswordOffset = uamx_member_offset(userPassword); uamInfo->uamNameOffset = uamx_member_offset(uamNameOffset); // //Now actually copy the data. // //01.16.02: Now pass maximum length to string copy routine. // UAM_PStrCopy( inServerName, uamInfo->serverName, sizeof(uamInfo->serverName) ); UAM_PStrCopy( inUserName, uamInfo->userName, sizeof(uamInfo->userName) ); UAM_PStrCopy( inPassword, uamInfo->userPassword, sizeof(uamInfo->userPassword) ); UAM_PStrCopy( (StringPtr)inUAMString, uamInfo->uamName, sizeof(uamInfo->uamName) ); } else { DbgPrint_((DBGBUFF, "Failed to allocated AFPX buffer! (%d)", MemError())); // //Couldn't allocate memory for structure. // return(memFullErr); } *outVolInfo = uamInfo; return(noErr); }