1 line
13 KiB
C
1 line
13 KiB
C
// ===========================================================================
|
|
// 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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|