1 line
32 KiB
C
1 line
32 KiB
C
// ===========================================================================
|
|
// UAMMain.cp © 1997-2000 Microsoft Corp. All rights reserved.
|
|
// ===========================================================================
|
|
// Main unit for Microsoft User Authentication Method.
|
|
//
|
|
// Notes:
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// Version History:
|
|
// ===========================================================================
|
|
// 04.22.97 MJC - Begin coding version 5.0.
|
|
// 05.22.97 MJC - Completed version 5.0d5, getting close.
|
|
// 02.21.98 MJC - Begin updating for AppleShare 3.8 and AFP/TCP.
|
|
// 03.02.98 MJC - First working version (5.0d12) with AS Client v3.8a1lawJ
|
|
// 03.26.98 MJC - Implemented change password. It works, but not when the
|
|
// password has expired on the server. I suspect a bug in
|
|
// in AppleShare Client 3.8a1LawL which doesn't open a session
|
|
// when the error code returned is not noErr.
|
|
// 03.31.98 MJC - First checkin into VSS database.
|
|
//
|
|
// Version 5.0d15:
|
|
// 04.13.98 MJC - Changed the way supported UAM's are recorded (bitmap vice
|
|
// a struct of booleans).
|
|
// - Change some error code determination code in UAM_OpenSession()
|
|
// and UAM_MSUAMContLogin().
|
|
// - Added version string at bottom of dialog window.
|
|
//
|
|
// Version 5.0d16:
|
|
// 04.30.98 MJC - Fixed bug in UAMDSNetwork.c where the AFP login command block
|
|
// would not always end on an even boundary.
|
|
// - Added some additional asserts to UAM_DSLoginMSUAM().
|
|
// - Changed instances of astring[0] to PSTR_LENGTH(astring).
|
|
//
|
|
// Version 5.0d17:
|
|
// 05.19.98 MJC - Updated for new ClientUAM.h from Apple. Now the main
|
|
// entry returns OSStatus vice OSErr.
|
|
//
|
|
// Version 5.0b2:
|
|
// 06.08.98 MJC - Added new event callback routine for AS and the Chooser
|
|
// in the login dialog filter.
|
|
// Version 5.0b3:
|
|
// 09.01.98 MJC - Fixed bug where null passwords weren't allowed.
|
|
// 10.23.98 MJC - Fixed bug where you could use cmd-g to select Guest
|
|
// radio even though it was disabled.
|
|
// - Can now use cut, copy and paste in User Name field.
|
|
// - Changed 'OK' button to 'Connect' to match Apple's UAM
|
|
// - Clicking on 'Registered User' when it is already
|
|
// doesn't cause a flash anymore or select the user name.
|
|
// 11.13.98 MJC - Added support for passing the actual encrypted password
|
|
// over the wire for cleartxt storage updating when
|
|
// changing password.
|
|
// MJC - Added support for notifying the user that their password
|
|
// is about to expire.
|
|
// 12.01.98 MJC - Fixed bug were I wasn't reversing the byte order of the
|
|
// returned password expiration time.
|
|
// 01.22.99 MJC - CheckGatedControls() would step 1 too far in the array.
|
|
// - Could not use escape key if username len maxed out.
|
|
// Version 5.0.1:
|
|
// 07.12.99 MJC - More problems with UAM_CheckGatedControls(), hopefully all
|
|
// fixed this time.
|
|
// Made small change in MS_VersionUserItem() so we compile
|
|
// under CW Pro 5.
|
|
// Version 5.0.2:
|
|
// 10.21.99 MJC - Fixed bug on double byte character OS's (CHX, JPN, etc)
|
|
// where first char in password was getting dropped.
|
|
// - Now select all the password text after a login failure.
|
|
// Version 5.0.3:
|
|
// 10.29.99 MJC - Fixed bug on international systems where hitting
|
|
// backspace would yield incorrect results (got rid of one
|
|
// char instead of the double byte char).
|
|
// - Related to fix above, change password field entry diaplay
|
|
// character to '*' instead of '¥'.
|
|
// Version 5.0.4:
|
|
// 11.17.99 MJC - Fixed bug in encrypt.c, wasn't locking resource handle,
|
|
// so password OWF was incorrectly generated.
|
|
// - SetupUAMEncrypt() was not returning a fail code
|
|
// if loading the data table failed.
|
|
// Version 5.0.5:
|
|
// 11.22.99 MJC - Put 2 0x00 bytes at the end of the initial login call for
|
|
// NT4 SP6.
|
|
// 12.01.99 MJC - Finished keychain support.
|
|
// - NOTE: You must now compile the MS UAM with Universal
|
|
// headers v3.3 or later.
|
|
// - Can finally build PPC! The MS UAM is now a safe FAT
|
|
// binary. So, it'll run natively on 68K and PPC.
|
|
// - Complete rewrite of password edit field handling. Now kicks
|
|
// butt! You can type just like any other text and should work
|
|
// better with foreign languages.
|
|
// - Made some changes to the dialog code in preparation
|
|
// for Carbon.
|
|
// 01.10.00 MJC - Now check for cmd key down when opening UAM so user can
|
|
// bypass keychain.
|
|
// 03.13.00 MJC - Removed about dialog.
|
|
// 03.15.00 MJC - Now check for MacOS 9 or > to see if keychain is available.
|
|
// - Now week load the Keychain.lib for compatibility with
|
|
// older systems.
|
|
// 03.20.00 MJC - Fixed bug: When changing password, wasn't checking for existance
|
|
// of keychain manager (caused -2802 error).
|
|
// Version 5.0.6:
|
|
// 06.11.00 MJC - Now give the option to replace keychains items that
|
|
// already exist. This caused problems when the user changed
|
|
// their password on another machine, there was no way to
|
|
// update the keychain item without doing it manually from the
|
|
// KeychainAccess control panel.
|
|
// Version 5.0.7:
|
|
// 09.06.00 MJC - Bug fix: keychain item shouldn't appear when guest selected
|
|
// - Bug fix: Don't allow white space as first char in user name, this
|
|
// involved redoing the gating logic in UAMDlogUtils.c.
|
|
// 09.28.00 - Bug fix: Allow null user name and password entries when
|
|
// guest login is enabled on server.
|
|
// ===========================================================================
|
|
|
|
#include <A4Stuff.h>
|
|
#include <SetupA4.h>
|
|
|
|
#include "UAMMain.h"
|
|
#include "UAMDebug.h"
|
|
#include "UAMUtils.h"
|
|
#include "UAMDialogs.h"
|
|
#include "UAMNetwork.h"
|
|
#include "UAMDSNetwork.h"
|
|
#include "UAMDLOGUtils.h"
|
|
#include "UAMKeychain.h"
|
|
|
|
//
|
|
//Global variables are declared here
|
|
//
|
|
Str32 gServerName;
|
|
Str32 gUserName;
|
|
Boolean gContextInited;
|
|
Boolean gGuestLogon;
|
|
Boolean gSupportsChngPwd;
|
|
Boolean gDoingIPConnection;
|
|
DialogPtr gDialog;
|
|
Str32 gAFPVersion;
|
|
long gSupportedUAMs;
|
|
ModalFilterUPP gDialogFilter;
|
|
ModalFilterUPP gPwdDialogFilter;
|
|
UserItemUPP gLineItem;
|
|
UserItemUPP gVersionItem;
|
|
Str32 gUAMVersionString;
|
|
Str32 gZoneName;
|
|
UInt32 gExpirationTime = 0;
|
|
OTAddress* gServerAddress = NULL;
|
|
EventCallbackPtr gEventCallbackUPP = NULL;
|
|
Boolean gTriedKeychain = false;
|
|
|
|
#if GENERATINGCFM
|
|
//We need to define __procinfo for Metrowerks' linker. This basically
|
|
//defines main. Without it, we'll get a link error.
|
|
ProcInfoType __procinfo = kPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
|
|
| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UAMArgs*)));
|
|
#endif
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ main()
|
|
// ---------------------------------------------------------------------------
|
|
// This is the main entry point for our UAM. This function is passed a
|
|
// pointer to a UAMArgs struct. This struct contains the function selector,
|
|
// call-backs, and many other things we need to do our stuff.
|
|
|
|
pascal OSStatus main(UAMArgs *inUAMArgs)
|
|
{
|
|
OSStatus theResult = noErr;
|
|
|
|
EnterCodeResource();
|
|
PrepareCallback();
|
|
|
|
switch(inUAMArgs->command)
|
|
{
|
|
case kUAMOpen:
|
|
theResult = MS_UAMOpen(inUAMArgs);
|
|
break;
|
|
|
|
case kUAMClose:
|
|
MS_UAMClose();
|
|
break;
|
|
|
|
case kUAMPWDlog:
|
|
theResult = MS_UAMPwdDialog(inUAMArgs);
|
|
break;
|
|
|
|
case kUAMLogin:
|
|
theResult = MS_UAMLogin(inUAMArgs);
|
|
break;
|
|
|
|
case kUAMVSDlog:
|
|
break;
|
|
|
|
case kUAMChgPass:
|
|
case kUAMChgPassDlg:
|
|
DbgPrint_((DBGBUFF, "Change password dialog must be implemented"));
|
|
theResult = kNotForUs;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
//If we get here then we were asked to handle a routine that
|
|
//we don't support. Return the appropriate error code.
|
|
//
|
|
|
|
DbgPrint_((DBGBUFF, "Unsupported function selector in MSUAM main() (%d)", inUAMArgs->command));
|
|
|
|
theResult = kNotForUs;
|
|
break;
|
|
}
|
|
|
|
ExitCodeResource();
|
|
|
|
return(theResult);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ MS_UAMOpen()
|
|
// ---------------------------------------------------------------------------
|
|
// This is called by the device package. It is not a required function but
|
|
// we use it to initialize our UAM code. Note that when we encounter an
|
|
// error we don't make an effort to clean up. Instead we return userCanceledErr
|
|
// in which case our UAMClose function will be called by AppleShare Client.
|
|
|
|
OSStatus MS_UAMOpen(UAMArgs *inUAMArgs)
|
|
{
|
|
short theUAMConfig = 0;
|
|
|
|
//
|
|
//Get the name of the server we want to log into.
|
|
//
|
|
UAM_PStrCopy(inUAMArgs->Opt.open.objectName, gServerName);
|
|
|
|
//
|
|
//Copy the zone name for. If it's NULL, then we
|
|
//don't have a zone name.
|
|
//
|
|
if (inUAMArgs->Opt.open.zoneName != NULL)
|
|
UAM_PStrCopy(inUAMArgs->Opt.open.zoneName, gZoneName);
|
|
else
|
|
gZoneName[0] = 0;
|
|
|
|
gContextInited = false; //Been through PwdDialog before?
|
|
gGuestLogon = false; //Is guest our logon choice?
|
|
gDoingIPConnection = false; //Default to AppleTalk support.
|
|
gDialog = NULL; //So we can see if we really got it.
|
|
gDialogFilter = NULL;
|
|
gPwdDialogFilter = NULL;
|
|
gLineItem = NULL;
|
|
gVersionItem = NULL;
|
|
gAFPVersion[0] = 0;
|
|
gUserName[0] = 0;
|
|
gServerAddress = inUAMArgs->Opt.open.srvrAddress;
|
|
gEventCallbackUPP = inUAMArgs->callbacks->EventCallbackUPP;
|
|
gTriedKeychain = false;
|
|
|
|
UAM_KCInitialize(inUAMArgs);
|
|
|
|
//
|
|
//Under PowerPC this is a pointer allocated. Under 68K, it just
|
|
//points to the function.
|
|
//
|
|
gDialogFilter = NewModalFilterProc(&UAM_DialogFilter);
|
|
if (gDialogFilter == NULL)
|
|
{
|
|
//
|
|
//We check for ptr validity. Note that we don't bother to
|
|
//clean up since we'll get a kUAMClose message next.
|
|
//
|
|
|
|
DbgPrint_((DBGBUFF, "Failed to allocate gDialogFilter"));
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
gPwdDialogFilter = NewModalFilterProc(&MS_PwdDialogFilter);
|
|
if (gPwdDialogFilter == NULL)
|
|
{
|
|
DbgPrint_((DBGBUFF, "Failed to allocate gPwdDialogFilter"));
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
gLineItem = NewUserItemProc(&UAM_FrameItem);
|
|
if (gLineItem == NULL)
|
|
{
|
|
DbgPrint_((DBGBUFF, "Failed to allocate gLineItem"));
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
gVersionItem = NewUserItemProc(&MS_VersionUserItem);
|
|
if (gVersionItem == NULL)
|
|
{
|
|
DbgPrint_((DBGBUFF, "Failed to allocate gVersionItem"));
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
//
|
|
//Get the AFP version and the default user name. This function finds
|
|
//a match which is the highest AFP version supported by both the client
|
|
//and server.
|
|
//
|
|
UAM_GetAFPVersionString(
|
|
inUAMArgs->Opt.open.srvrInfo,
|
|
inUAMArgs->callbacks,
|
|
gAFPVersion,
|
|
gUserName );
|
|
|
|
//
|
|
//gUserName can be null, we just capture here during debugging to
|
|
//ensure we're getting the name properly.
|
|
//
|
|
Assert_(PSTR_LENGTH(gUserName) != 0);
|
|
Assert_(PSTR_LENGTH(gAFPVersion) != 0);
|
|
|
|
if (PSTR_LENGTH(gAFPVersion) == 0)
|
|
{
|
|
//
|
|
//No AFPVersion, no logon...
|
|
//
|
|
|
|
UAM_ReportError(uamErr_NoAFPVersion);
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
gSupportsChngPwd = ((inUAMArgs->Opt.open.srvrInfo->fFlags & kSupportsChngPswd) != 0);
|
|
|
|
//
|
|
//Determine what connection method we are using, IP or AppleTalk. Basically,
|
|
//if the client supports IP and the address type is IP, then we have
|
|
//a TCP connection.
|
|
//
|
|
if (inUAMArgs->Opt.open.srvrInfo->fFlags & kSupportsTCPIP)
|
|
{
|
|
if (inUAMArgs->Opt.open.srvrAddress->fAddressType == AF_INET)
|
|
{
|
|
gDoingIPConnection = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
//Get the list of supported UAMs from a utility routine. This data
|
|
//is necessary in the password dialog code.
|
|
//
|
|
UAM_GetSupportedUAMS(
|
|
(ServerInfoReplyBlock *)inUAMArgs->Opt.open.srvrInfo,
|
|
&gSupportedUAMs );
|
|
|
|
//
|
|
//We should never get here if the following is false, but we
|
|
//check just to be on the safe side.
|
|
//
|
|
if ( ((gSupportedUAMs & kMSUAMSupported) == 0) &&
|
|
((gSupportedUAMs & kMSUAM_V2_Supported) == 0) )
|
|
{
|
|
Assert_((gSupportedUAMs & kMSUAMSupported) != 0);
|
|
|
|
UAM_ReportError(afpBadUAM);
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
UAM_VersionString(gUAMVersionString);
|
|
|
|
//
|
|
//This is how we tell AppleShare what our UAM supports. We have
|
|
//our own password dialog, we support change password, and we
|
|
//use our own change password dialog.
|
|
//
|
|
|
|
theUAMConfig |= BIT_0; //Custom login dialog
|
|
theUAMConfig |= BIT_2; //We support change password
|
|
theUAMConfig |= BIT_3; //Custom change password dialog
|
|
|
|
inUAMArgs->result = theUAMConfig;
|
|
|
|
return(noErr);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ MS_UAMClose()
|
|
// ---------------------------------------------------------------------------
|
|
// Like UAMOpen, UAMClose has no specific purpose as defined by the device
|
|
// manager. We use it to clean up our allocated storage and globals.
|
|
|
|
void MS_UAMClose(void)
|
|
{
|
|
if (gDialog != NULL)
|
|
{
|
|
//
|
|
//If we put up our login dialog, get rid of it.
|
|
//
|
|
UAM_DisposeDialog(gDialog);
|
|
}
|
|
|
|
if (gDialogFilter != NULL) DisposeRoutineDescriptor(gDialogFilter);
|
|
if (gLineItem != NULL) DisposeRoutineDescriptor(gLineItem);
|
|
if (gPwdDialogFilter != NULL) DisposeRoutineDescriptor(gPwdDialogFilter);
|
|
if (gVersionItem != NULL) DisposeRoutineDescriptor(gVersionItem);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ MS_VersionUserItem()
|
|
// ---------------------------------------------------------------------------
|
|
// Custom user item routine to display UAM version number.
|
|
|
|
pascal void MS_VersionUserItem(DialogPtr inDialog, DialogItemIndex inItem)
|
|
{
|
|
short theFont, theSize;
|
|
Rect theItemRect;
|
|
|
|
EnterCallback();
|
|
|
|
theFont = inDialog->txFont;
|
|
theSize = inDialog->txSize;
|
|
|
|
TextFont(kFontIDGeneva);
|
|
TextSize(9);
|
|
|
|
theItemRect = UAM_GetItemRect(inDialog, inItem);
|
|
|
|
TETextBox(
|
|
&gUAMVersionString[1],
|
|
PSTR_LENGTH(gUAMVersionString),
|
|
&theItemRect,
|
|
teJustRight );
|
|
|
|
TextFont(theFont);
|
|
TextSize(theSize);
|
|
|
|
ExitCallback();
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ MS_PwdDialogFilter()
|
|
// ---------------------------------------------------------------------------
|
|
// Filter function for the password dialog. We have this so we can capture
|
|
// command keys and keep length requirements for the user name in the login
|
|
// dialog.
|
|
|
|
pascal Boolean MS_PwdDialogFilter(DialogPtr inDialog, EventRecord *inEvent, short *inItem)
|
|
{
|
|
short theCode;
|
|
Str255 theString;
|
|
Boolean theResult = false;
|
|
|
|
EnterCallback();
|
|
|
|
if (inEvent->what == keyDown)
|
|
{
|
|
theCode = (inEvent->message & charCodeMask);
|
|
|
|
if (inEvent->modifiers & cmdKey)
|
|
{
|
|
switch(theCode)
|
|
{
|
|
case 'g':
|
|
case 'G':
|
|
*inItem = DITEM_GuestRadio;
|
|
theResult = true;
|
|
break;
|
|
|
|
case 'r':
|
|
case 'R':
|
|
*inItem = DITEM_RegRadio;
|
|
theResult = true;
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
*inItem = DITEM_ChangePwd;
|
|
theResult = true;
|
|
break;
|
|
|
|
case 'a':
|
|
case 'A':
|
|
*inItem = DITEM_Keychain;
|
|
theResult = true;
|
|
break;
|
|
|
|
//
|
|
//Handle edit commands from the user. We don't allow any
|
|
//editing commands in the password field. This mimicks
|
|
//Apple's own UAM's.
|
|
//
|
|
|
|
case 'c':
|
|
case 'C':
|
|
if ((((DialogPeek)inDialog)->editField + 1) != DITEM_Password) {
|
|
DialogCopy(inDialog);
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
case 'V':
|
|
if ((((DialogPeek)inDialog)->editField + 1) != DITEM_Password) {
|
|
DialogPaste(inDialog);
|
|
}
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
if ((((DialogPeek)inDialog)->editField + 1) != DITEM_Password) {
|
|
DialogCut(inDialog);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//Don't allow more than UAM_USERNAMELEN maximum characters in edit field.
|
|
//
|
|
|
|
if ((((DialogPeek)inDialog)->editField + 1) == DITEM_UserName)
|
|
{
|
|
UAM_GetText(inDialog, DITEM_UserName, (Str255 *)&theString);
|
|
|
|
switch(theCode)
|
|
{
|
|
case UAMKey_BackDel:
|
|
case UAMKey_Left:
|
|
case UAMKey_Right:
|
|
case UAMKey_Return:
|
|
case UAMKey_Enter:
|
|
case UAMKey_Escape:
|
|
break;
|
|
|
|
default:
|
|
if (PSTR_LENGTH(theString) >= UAM_USERNAMELEN)
|
|
{
|
|
SysBeep(1);
|
|
|
|
inEvent->what = nullEvent;
|
|
theResult = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gEventCallbackUPP)
|
|
{
|
|
//
|
|
//If we're not handling the event ourselves, then call the
|
|
//event callback which gives AS and the Chooser a chance
|
|
//to update it's windows, etc.
|
|
//
|
|
|
|
#if GENERATING68K
|
|
|
|
gEventCallbackUPP(inEvent);
|
|
|
|
#else
|
|
|
|
CallUniversalProc(gEventCallbackUPP, kEventCallbackProcInfo, inEvent);
|
|
|
|
#endif
|
|
}
|
|
}
|
|
|
|
ExitCallback();
|
|
|
|
return(theResult);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ MS_UAMPwdDialog()
|
|
// ---------------------------------------------------------------------------
|
|
// This is where we put up our password dialog. The buffers pointed to by
|
|
// 'inUserName' and 'inPassword' end up getting passed directly to the
|
|
// UAMLogin function.
|
|
//
|
|
// The buffer passed for the user name and password is 64 bytes long. Don't
|
|
// use more than that!
|
|
|
|
OSStatus MS_UAMPwdDialog(UAMArgs *inUAMArgs)
|
|
{
|
|
short theItem, x;
|
|
Str255 theStr;
|
|
OSStatus theError = noErr;
|
|
Boolean theLoop = true;
|
|
|
|
Assert_(gDialogFilter != NULL);
|
|
Assert_(gPwdDialogFilter != NULL);
|
|
Assert_(gLineItem != NULL);
|
|
|
|
//
|
|
//Determine which user name to use, the default or the
|
|
//one supplied by the client (if any). gUserName is filled
|
|
//in originally during the UAMOpen call.
|
|
//
|
|
if (PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName) != 0)
|
|
{
|
|
UAM_PStrCopy(inUAMArgs->Opt.pwDlg.userName, gUserName);
|
|
}
|
|
|
|
//
|
|
//If we already tried the keychain and failed, we don't want
|
|
//to try again or we'll loop forever. Give the user a chance
|
|
//to enter the correct name and password.
|
|
//
|
|
//NOTE: We check to see if the cmd key is down, if it is, then
|
|
//we bypass the keychain stuff alltogether. Maybe the user wants
|
|
//to change his password!?!?!
|
|
//
|
|
if ((gTriedKeychain == false) && (UAM_KCAvailable()) && (!UAM_KeyDown(KEY_Command)))
|
|
{
|
|
gTriedKeychain = true;
|
|
|
|
if ( (PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName)) &&
|
|
(PSTR_LENGTH(inUAMArgs->Opt.pwDlg.password)) )
|
|
{
|
|
//
|
|
//We were supplied a username and password by the AFP
|
|
//client. This means the user clicked a keychain entry.
|
|
//
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
theError = UAM_KCFindAppleSharePassword(
|
|
gUserName,
|
|
inUAMArgs->Opt.pwDlg.password,
|
|
gServerName,
|
|
NULL
|
|
);
|
|
|
|
if (theError == noErr)
|
|
{
|
|
DbgPrint_((DBGBUFF, "Pswd found via MSUAM keychain calls;g"));
|
|
|
|
//
|
|
//Fill in the user name for the UAMArgs.
|
|
//
|
|
if (PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName) == 0)
|
|
{
|
|
UAM_PStrCopy(gUserName, inUAMArgs->Opt.pwDlg.userName);
|
|
}
|
|
|
|
//
|
|
//A password was found so try to logon.
|
|
//
|
|
goto exit;
|
|
}
|
|
else if ( (theError != errKCItemNotFound) &&
|
|
(theError != userCanceledErr) )
|
|
{
|
|
//
|
|
//Only report "real" errors.
|
|
//
|
|
UAM_ReportError(theError);
|
|
}
|
|
}
|
|
}
|
|
else if ((UAM_KCAvailable()) && (UAM_KeyDown(KEY_Command)))
|
|
{
|
|
//
|
|
//If the user is holding the cmd key down, then we don't want to
|
|
//try the keychain the next time through either.
|
|
//
|
|
gTriedKeychain = true;
|
|
}
|
|
|
|
//
|
|
//Display the server name in the dialog title text
|
|
//which is located at the top of the dialog. This must be
|
|
//done even if we've been here before.
|
|
//
|
|
|
|
ParamText(gServerName, NULL, NULL, NULL);
|
|
|
|
//
|
|
//If we haven't been through here before, then we need to do
|
|
//all the prep work.
|
|
//
|
|
|
|
if (!gContextInited)
|
|
{
|
|
gDialog = UAM_NewDialog(DLOG_Login, true);
|
|
if (gDialog == NULL)
|
|
{
|
|
//
|
|
//If we couldn't get the dialog, then we're either out
|
|
//of memory or the resource couldn't be found.
|
|
//
|
|
|
|
theError = MemError();
|
|
if (theError == noErr)
|
|
theError = ResError();
|
|
if (theError == noErr)
|
|
theError = resNotFound;
|
|
|
|
UAM_ReportError(theError);
|
|
return(userCanceledErr);
|
|
}
|
|
|
|
//
|
|
//Set up the default user name and password (if any). If a user name
|
|
//exists, then make the password field the active field ready for input.
|
|
//
|
|
|
|
UAM_SetUpUserItem(gDialog, DITEM_Line, gLineItem, userItem);
|
|
UAM_SetUpUserItem(gDialog, DITEM_Version, gVersionItem, userItem);
|
|
|
|
//
|
|
//Put in some extra info in the dialog for debugging.
|
|
//
|
|
#ifdef UAMDebug
|
|
Str255 theConnString;
|
|
|
|
if (gDoingIPConnection)
|
|
UAM_PStrCopy(PSTR_TCPConnection, theConnString);
|
|
else
|
|
UAM_PStrCopy(PSTR_AppleTalkConnection, theConnString);
|
|
|
|
if (gSupportedUAMs & kMSUAM_V2_Supported)
|
|
UAM_AppendPStr(theConnString, "\p (MS2.0)", sizeof(Str255));
|
|
else
|
|
UAM_AppendPStr(theConnString, "\p (MS1.0)", sizeof(Str255));
|
|
|
|
#if GENERATINGCFM
|
|
UAM_AppendPStr(theConnString, "\p[PPC]", sizeof(Str255));
|
|
#else
|
|
UAM_AppendPStr(theConnString, "\p[68K]", sizeof(Str255));
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
//Let the client know what connection method is being used to
|
|
//connect to the server.
|
|
//
|
|
if (gDoingIPConnection)
|
|
{
|
|
#ifdef UAMDebug
|
|
UAM_SetText(gDialog, DITEM_Method, theConnString);
|
|
#else
|
|
UAM_SetText(gDialog, DITEM_Method, PSTR_TCPConnection);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef UAMDebug
|
|
UAM_SetText(gDialog, DITEM_Method, theConnString);
|
|
#else
|
|
UAM_SetText(gDialog, DITEM_Method, PSTR_AppleTalkConnection);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//If we've not been here before, then we want to use the user name
|
|
//entered in the Sharing Setup Control Panel (or Chooser).
|
|
//
|
|
|
|
if (PSTR_LENGTH(gUserName) != 0)
|
|
{
|
|
UAM_SetText(gDialog, DITEM_UserName, gUserName);
|
|
SelectDialogItemText(gDialog, DITEM_Password, 0, 64);
|
|
}
|
|
else
|
|
{
|
|
UAM_HiliteItem(gDialog, 1, 255);
|
|
}
|
|
|
|
//
|
|
//Now we set up the guest and registered user radio buttons and the
|
|
//change password button as determined by UAM_GetServerInfo().
|
|
//
|
|
|
|
if (!gSupportsChngPwd) {
|
|
UAM_HiliteItem(gDialog, DITEM_ChangePwd, 255);
|
|
}
|
|
else {
|
|
UAM_GateControl(gDialog, DITEM_ChangePwd, DITEM_UserName);
|
|
}
|
|
|
|
if (!(gSupportedUAMs & kGuestSupported))
|
|
{
|
|
//
|
|
//No guest support, we don't need the guest radio button.
|
|
//
|
|
UAM_HiliteItem(gDialog, DITEM_GuestRadio, 255);
|
|
|
|
//
|
|
//If guest is not supported, then we gate the connect
|
|
//button to the username text field.
|
|
//
|
|
UAM_GateControl(gDialog, DITEM_Connect, DITEM_UserName);
|
|
}
|
|
|
|
//
|
|
//Set the initial radio for the default/current login method.
|
|
//
|
|
|
|
if (gGuestLogon)
|
|
{
|
|
UAM_SetCValue(gDialog, DITEM_GuestRadio, 1);
|
|
UAM_SetCValue(gDialog, DITEM_RegRadio, 0);
|
|
|
|
UAM_HiliteItem(gDialog, DITEM_ChangePwd, 255);
|
|
|
|
for (x = DITEM_FirstHideItem; x <= DITEM_LastHideItem; x++) {
|
|
HideDialogItem(gDialog, x);
|
|
}
|
|
|
|
UAM_HiliteItem(gDialog, 1, 0);
|
|
}
|
|
else {
|
|
UAM_SetCValue(gDialog, DITEM_RegRadio, 1);
|
|
}
|
|
|
|
UAM_SetBulletItem(gDialog, DITEM_Password, UAM_CLRTXTPWDLEN);
|
|
UAM_SupportCmdKeys(gDialog, false);
|
|
|
|
//
|
|
//Set our custom filter function so we can handle command keys and
|
|
//manage user name maximum string length.
|
|
//
|
|
UAM_SetCustomFilterProc(gDialog, gPwdDialogFilter);
|
|
|
|
//
|
|
//If the client is not allowed to save password for this server,
|
|
//then we gray out the keychain checkbox.
|
|
//
|
|
if (UAM_KCAvailable() == false)
|
|
{
|
|
UAM_HiliteItem(gDialog, DITEM_Keychain, 255);
|
|
}
|
|
else if (gTriedKeychain)
|
|
{
|
|
UAM_SetBulletText(gDialog, DITEM_Password, inUAMArgs->Opt.pwDlg.password);
|
|
SelectDialogItemText(gDialog, DITEM_Password, 0, 64);
|
|
}
|
|
|
|
//
|
|
//This flag lets up know that we've initialized our login dialog
|
|
//and that we don't need to do it again when/if we come here again.
|
|
//
|
|
|
|
gContextInited = true;
|
|
}
|
|
else {
|
|
UAM_SetText(gDialog, DITEM_UserName, inUAMArgs->Opt.pwDlg.userName);
|
|
UAM_SetBulletText(gDialog, DITEM_Password, inUAMArgs->Opt.pwDlg.password);
|
|
|
|
//
|
|
//Hilite the password selection.
|
|
//
|
|
SelectDialogItemText(gDialog, DITEM_Password, 0, 64);
|
|
|
|
InvalRect(&gDialog->portRect);
|
|
}
|
|
|
|
do
|
|
{
|
|
ModalDialog(gDialogFilter, &theItem);
|
|
|
|
//
|
|
//Check gated controls, disable them if their text item
|
|
//counterpart has no text.
|
|
//
|
|
|
|
UAM_CheckGatedControls(gDialog);
|
|
|
|
switch(theItem)
|
|
{
|
|
case DITEM_OK:
|
|
gGuestLogon = (UAM_GetCValue(gDialog, DITEM_GuestRadio) != 0);
|
|
theError = noErr;
|
|
theLoop = false;
|
|
|
|
if (gGuestLogon)
|
|
{
|
|
inUAMArgs->Opt.pwDlg.userName[0] = 0;
|
|
inUAMArgs->Opt.pwDlg.password[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
UAM_GetBulletBuffer( gDialog,
|
|
DITEM_Password,
|
|
inUAMArgs->Opt.pwDlg.password );
|
|
|
|
UAM_GetText( gDialog,
|
|
DITEM_UserName,
|
|
(Str255 *)inUAMArgs->Opt.pwDlg.userName );
|
|
}
|
|
break;
|
|
|
|
case DITEM_Cancel:
|
|
//
|
|
//VERSION 5.0: To force cancellation, we pass userCanceledError(-128)
|
|
//back to the Chooser. The old UAM would pass back dsForcedQuit which
|
|
//is the wrong value. This would cause an error dialog when cancelling.
|
|
//
|
|
|
|
theError = userCanceledError;
|
|
theLoop = false;
|
|
break;
|
|
|
|
case DITEM_GuestRadio:
|
|
//
|
|
//Set up the controls in the dialog for guest login. We don't
|
|
//need the user name and password items, so hide them from
|
|
//the user. We must explicitly enable the 'OK' button since
|
|
//it may have been disabled by the gate stuff.
|
|
//
|
|
|
|
if (UAM_IsActive(gDialog, DITEM_GuestRadio))
|
|
{
|
|
UAM_SetCValue(gDialog, DITEM_GuestRadio, 1);
|
|
UAM_SetCValue(gDialog, DITEM_RegRadio, 0);
|
|
|
|
//UAM_HiliteItem(gDialog, DITEM_ChangePwd, 255);
|
|
|
|
for (x = DITEM_FirstHideItem; x <= DITEM_LastHideItem; x++) {
|
|
HideDialogItem(gDialog, x);
|
|
}
|
|
|
|
//
|
|
//Now hide the keychain checkbox
|
|
//
|
|
UAM_SetCValue(gDialog, DITEM_Keychain, 0);
|
|
HideDialogItem(gDialog, DITEM_Keychain);
|
|
|
|
UAM_StopGate(gDialog, DITEM_Connect);
|
|
}
|
|
break;
|
|
|
|
case DITEM_RegRadio:
|
|
//
|
|
//Now we need all the items back that were hidden above, make
|
|
//them visible.
|
|
//
|
|
if (UAM_GetCValue(gDialog, DITEM_RegRadio) <= 0)
|
|
{
|
|
UAM_SetCValue(gDialog, DITEM_GuestRadio, 0);
|
|
UAM_SetCValue(gDialog, DITEM_RegRadio, 1);
|
|
|
|
for (x = DITEM_FirstHideItem; x <= DITEM_LastHideItem; x++) {
|
|
ShowDialogItem(gDialog, x);
|
|
}
|
|
|
|
//
|
|
//Make the keychain item reaappear.
|
|
//
|
|
ShowDialogItem(gDialog, DITEM_Keychain);
|
|
|
|
UAM_GetText(gDialog, DITEM_UserName, &theStr);
|
|
SelectDialogItemText(gDialog, DITEM_UserName, 0, 32767);
|
|
|
|
if ((gSupportsChngPwd) && (theStr[0] != 0)) {
|
|
UAM_HiliteItem(gDialog, DITEM_ChangePwd, 0);
|
|
}
|
|
|
|
//
|
|
//Check to see if guest is supported or not so we know if
|
|
//we need to gate the connect button.
|
|
//
|
|
if (!(gSupportedUAMs & kGuestSupported))
|
|
{
|
|
UAM_GateControl(gDialog, DITEM_Connect, DITEM_UserName);
|
|
UAM_CheckGatedControls(gDialog);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DITEM_ChangePwd:
|
|
UAM_GetBulletBuffer( gDialog,
|
|
DITEM_Password,
|
|
inUAMArgs->Opt.pwDlg.password );
|
|
UAM_GetText( gDialog,
|
|
DITEM_UserName,
|
|
(Str255 *)inUAMArgs->Opt.pwDlg.userName );
|
|
|
|
theError = UAM_ChangePwd(inUAMArgs);
|
|
switch(theError)
|
|
{
|
|
case CHNGPSWD_USER_CANCELED:
|
|
break;
|
|
|
|
case CHNGPSWD_UPDATE_KEYCHAIN:
|
|
//
|
|
//We need to re-add the keychain item with the
|
|
//correct password. Flag it by checking the box.
|
|
//
|
|
UAM_SetCValue(gDialog, DITEM_Keychain, 1);
|
|
|
|
//
|
|
//Just fall on through and handle the normal case.
|
|
//
|
|
|
|
case CHNGPSWD_NOERR:
|
|
//
|
|
//Set the password field and buffer with the new password in case
|
|
//we end back here later.
|
|
//
|
|
|
|
UAM_SetBulletText(gDialog, DITEM_Password, inUAMArgs->Opt.pwDlg.password);
|
|
|
|
theError = noErr;
|
|
theLoop = false;
|
|
break;
|
|
|
|
default:
|
|
UAM_ReportError(theError);
|
|
|
|
//
|
|
//Because we use ParamText() we must manually force an update
|
|
//of the dialog or things won't redraw properly.
|
|
//
|
|
InvalRect(&gDialog->portRect);
|
|
break;
|
|
}
|
|
|
|
//
|
|
//Must reset our user's name since UAM_ChangePwd() uses ParamText()
|
|
//to set some strings of it's own.
|
|
//
|
|
|
|
ParamText(gServerName, NULL, NULL, NULL);
|
|
break;
|
|
|
|
case DITEM_Keychain:
|
|
UAM_ToggleControl(gDialog, DITEM_Keychain);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}while(theLoop);
|
|
|
|
exit:
|
|
return(theError);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ MS_UAMLogin()
|
|
// ---------------------------------------------------------------------------
|
|
// This routine does the actual logging onto the server.
|
|
|
|
OSStatus MS_UAMLogin(UAMArgs *inUAMArgs)
|
|
{
|
|
OSStatus theError = noErr;
|
|
Boolean theLoop = true;
|
|
CursHandle theCursor;
|
|
|
|
Assert_(inUAMArgs != NULL);
|
|
|
|
do
|
|
{
|
|
theCursor = GetCursor(watchCursor);
|
|
SetCursor(*theCursor);
|
|
|
|
if (gGuestLogon) {
|
|
theError = UAM_DSLoginGuest(inUAMArgs);
|
|
}
|
|
else {
|
|
theError = UAM_DSLoginMSUAM(inUAMArgs);
|
|
}
|
|
|
|
if (theError != noErr)
|
|
{
|
|
//
|
|
//For whatever reason, we couldn't log into the server, handle the most
|
|
//basic errors and try to logon again by presenting the login dialog
|
|
//again. Otherwise, exit...
|
|
//
|
|
|
|
UAM_ReportError(theError);
|
|
|
|
switch(theError)
|
|
{
|
|
case afpNTPasswordExpired:
|
|
case afpPwdExpiredErr:
|
|
UAM_CloseSession(inUAMArgs);
|
|
|
|
case afpUserNotAuth:
|
|
case afpParmErr:
|
|
case afpNTAccountDisabled:
|
|
case afpNTInvalidWorkstation:
|
|
case afpNTInvalidLogonHours:
|
|
if (MS_UAMPwdDialog(inUAMArgs) != noErr)
|
|
return(userCanceledErr);
|
|
break;
|
|
|
|
default:
|
|
theLoop = false;
|
|
theError = userCanceledErr;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((gSupportedUAMs & kMSUAM_V2_Supported) && (!gGuestLogon))
|
|
{
|
|
//
|
|
//Check for password expiration at this point.
|
|
//
|
|
UInt32 theDaysTillExpiration = (((gExpirationTime / 60) / 60) / 24);
|
|
|
|
if (theDaysTillExpiration <= MINIMUM_DAYS_TILL_EXPIRATION)
|
|
{
|
|
//
|
|
//The password is going to expire within MINIMUM_DAYS_TILL_EXPIRATION,
|
|
//post the nofication dialog.
|
|
//
|
|
|
|
UAM_ChangePasswordNotificationDlg(theDaysTillExpiration);
|
|
}
|
|
}
|
|
|
|
if (UAM_KCAvailable())
|
|
{
|
|
//
|
|
//If the user is allowed to save their password and
|
|
//the keychain check box is checked, save the current
|
|
//credentials to the keychain.
|
|
//
|
|
if (UAM_GetCValue(gDialog, DITEM_Keychain) > 0)
|
|
{
|
|
theError = UAM_KCSavePassword(
|
|
inUAMArgs->Opt.auth.userName,
|
|
inUAMArgs->Opt.auth.password,
|
|
gServerName
|
|
);
|
|
|
|
if ((theError != noErr) && (theError != userCanceledErr))
|
|
{
|
|
if (theError == errKCDuplicateItem)
|
|
{
|
|
Int16 theResponse;
|
|
|
|
//
|
|
//A duplicate item exists, see if the user wants
|
|
//to replace it.
|
|
//
|
|
|
|
theResponse = UAM_AskQuestion(UAM_ReplaceKeyQuestion);
|
|
|
|
if (theResponse == ALRT_YES)
|
|
{
|
|
//
|
|
//The user asked us to replace the item. Try one
|
|
//more time to add the keychain item.
|
|
//
|
|
theError = UAM_KCDeleteItem(
|
|
inUAMArgs->Opt.auth.userName,
|
|
gServerName
|
|
);
|
|
|
|
if (theError == noErr)
|
|
{
|
|
theError = UAM_KCSavePassword(
|
|
inUAMArgs->Opt.auth.userName,
|
|
inUAMArgs->Opt.auth.password,
|
|
gServerName
|
|
);
|
|
|
|
if (theError != noErr)
|
|
{
|
|
//
|
|
//We errored out, nothing to do but report it.
|
|
//
|
|
UAM_ReportError(theError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UAM_ReportError(theError);
|
|
}
|
|
|
|
//
|
|
//We do not want to pass back any keychain error codes to
|
|
//the AFP client!
|
|
//
|
|
theError = noErr;
|
|
}
|
|
}
|
|
}
|
|
|
|
theLoop = false;
|
|
}
|
|
|
|
}while(theLoop);
|
|
|
|
return(theError);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|