You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1 lines
14 KiB
1 lines
14 KiB
// ===========================================================================
// 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);
}
|