// ===========================================================================//	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);		}	}	//	//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);g", 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);g", 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);g", 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);g", 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);g", 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", 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", 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)			{				UAM_PStrCopy(					(StringPtr)(((UInt32)theUamInfo) + theUamInfo->userPasswordOffset),					inPassword				);			}						theStatus = noErr;		}	}	else if (theStatus != errKCItemNotFound)	{		//		//For debugging only, we print out the error code.		//		DbgPrint_((DBGBUFF, "KCFindAppleSharePassword() failed (%d)", 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);		}		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.		//		UAM_PStrCopy(inServerName, uamInfo->serverName);		UAM_PStrCopy(inUserName, uamInfo->userName);		UAM_PStrCopy(inPassword, uamInfo->userPassword);		UAM_PStrCopy((StringPtr)inUAMString, (StringPtr)uamInfo->uamName);	}	else	{		DbgPrint_((DBGBUFF, "Failed to allocated AFPX buffer! (%d)", MemError()));				//		//Couldn't allocate memory for structure.		//		return(memFullErr);	}		*outVolInfo = uamInfo;		return(noErr);}