Source code of Windows XP (NT5)
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
32 KiB

// =========================================================================== // 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); }