/* File: PPCAuth.c Copyright: © 1989-1992 by Apple Computer, Inc., all rights reserved. Change History (most recent first): <36> 4/14/92 BBM : Remove unfinished PPC code that is under the contitional ÒTheFututeÓ, and remove the conditional ÒCubeEÓ since that is reality. Remove conditionals, since all they do is confuse. <35> 10/4/91 JSM Change PsychoticFarmerOrLater conditionals to TheFuture. <34> 9/29/91 DTY Conditionalize <32> and <33> out of the CubeE build. <33> 6/26/91 EMT Fix length check on user name bug <32> 6/10/91 EMT Provide internal call to create userRefnums <31> 12/13/90 EMT Support 0 Length String to mean DefaultPrompt in StartSecureSession, changed default prompt to have smart quotes, and use ParamText for internationalization. <30> 11/13/90 EMT Have StartSecureSession preflight for idiot error in the same fashion PPCStart does, and fix noUserNameBug, changed pGlobal to ppcglobptr, for consistency. <29> 11/8/90 EMT Fix bug in GetDefaultUser, change how userrefnums are handled, so that the limit is dynamic. Fixed bug so that user reference numbers are tied to Name/Password pair, not just Name. <28> 11/6/90 EMT minor bug fixes. <27> 10/24/90 EMT Update error codes <26> 10/19/90 JSM Remove ppcPromptForUser(), no longer used. <25> 10/11/90 EMT Make changes to obsolete CompletionGlue.a <24> 9/21/90 EMT Update constants, types, and field names as dictated by PPCToolBox.h <23> 9/14/90 VC Changed all reference to UserREf to an unsigned long. <22> 9/4/90 EMT Eric's Changes rolled in here! <21> 8/23/90 VC In PPCStartSecure after CheckLocName set the error code in the cmdResult field of the PB. <20> 8/21/90 VC In StartCesureSession added the extra check from the return of CheckLocName for error codes. <19> 8/18/90 dba get rid of code to center the PPCAlert alert, since the Window Mgr. does it; also simplify other code surrounding the call to the alert, including getting rid of the cursor adjusting code, since the standard dialog filter now does that <18> 8/10/90 S Included making a default string for login dialog. <17> 6/28/90 S Fixed login problem (guestnotAllowed Error) when actuallu name and password are nil. <16> 6/11/90 S Change Divide by 2 into Shift Operation in promptCallBack procedure. <15> 6/11/90 S Change promptCallBcak to promptCallBack. <14> 6/1/90 VC Addde mixupPswd to un-scramble password from the user record. <13> 4/26/90 VC Returning the proper selectItem from the callback routine <12> 4/18/90 JSM Include String.h for memset definition, add prototype for random, #ifdef some local variables for INIT version only, don't HLock things for BlockMove, and other miscellaneous code size reductions. <11> 4/18/90 JSM #ifdef openPPCRf for INIT version only. <10> 4/10/90 S Error Code Fix for LogIN Cancel. <9> 3/20/90 S To Fix Some Error Codes. <7> 2/27/90 S use new user&group.h file. <2.1> 1/22/90 CVC Remove "STRATCALLBACK" and pass proc pointer when calling PromptForUser in PPcStartSecure. <2> 12/28/89 dba use quotes around the names of local header files, brackets around the names of header files from the Interfaces or Internals directories <1.4> 12/7/89 CVC Added centering of the alert window. <1.3> 11/28/89 CVC Added ppcStartSecure call. <1.2> 11/17/89 CVC Added a conditioned-dompile in PPCPromptForUser. <1.1> 11/7/89 CVC Changed PPCPromptForUser to allow specifying User names <1.0> 10/12/89 CVC Adding Access Control for the first time. Old Revision History: 9/20/89 Victor Created. 10/12/89 Victor Moved the initAuth call to ppcLoader.c */ #include "PPCCommon.h" #include "UserGroup.h" #include #include #include #include #include // for memset definition #include /*--------------------------------------------------------------------------------------------------- Constants used only in this file ---------------------------------------------------------------------------------------------------*/ #define ppcErrStrings -16411 #define rPPCAlert -16411 /*--------------------------------------------------------------------------------------------------- Prototypes used in this file only. ---------------------------------------------------------------------------------------------------*/ STATIC void GetUserCompletion(void); STATIC Boolean UserRefNumCompare(UserEntryPtr theUserEntry,unsigned long userRef); STATIC UserEntryPtr FindUserByNameAndPassword(StringPtr userName,StringPtr password); STATIC UserEntryPtr GetFreeEntry(void); STATIC OSErr promptCallBack (StringPtr password, StringPtr name, short method, PPCStartPBPtr pb); long random(); extern pascal KeySched (char*, long*); extern pascal Encode (long*, long, long **); /*--------------------------------------------------------------------------------------------------- mixupPswd This is a very silly routine to mix up passwords in the user and groups file. It has the sole advantage of making it hard to figure out the algorithm even if a large number of users have a blank password (I hope). Since this is an encryption routine the code is suitable cryptic. ---------------------------------------------------------------------------------------------------*/ void mixupPswd (char * PassBlk, long UserID, Boolean Encrypt) { char * mask = "rpcgtprk"; char key = (char)UserID; short i; if (Encrypt) { for (i=0; i<8; i++) PassBlk[i] = key = PassBlk[i] ^ mask[i] ^ key; } else { for (i=7; i>0; i--) PassBlk[i] ^= mask[i] ^ PassBlk[i-1]; PassBlk[0] ^= mask[0] ^ key; } } /************************************************************************************ * getUserRec ************************************************************************************/ getUserRec (UserRecPb *urec, int keyID, char *keyName, int (*ppcComplete)(), short refNum) { urec->btPb.ioBTKeyPtr = (Ptr)&(urec->key); BldFSKey( keyID, keyName, urec->btPb.ioBTKeyPtr); // make the key urec->btPb.ioBuffer= (Ptr)&(urec->u.record); // set pointer to record urec->btPb.ioReqCount = sizeof (urec->u); urec->btPb.ioRefNum= refNum; urec->btPb.ioCompletion= (ProcPtr)GetUserCompletion; urec->btPb.ioBTHint1= 0; // zero Hint field urec->usrCompletion= ppcComplete; // set up completion routine BTSEARCHPPC( &urec->btPb, 1); // dispatch the call } /************************************************************************************ * GetUserCompletion ************************************************************************************/ STATIC void GetUserCompletion(void) { UserRecPb *urec; long keyArray[32]; long *temp; urec = GetA0(); // see if we found user, if we do generate a random number and encrypt // the random number. if (!urec->btPb.ioResult) { mixupPswd (urec->u.record.Password, urec->u.record.UserID, 0); urec->random[0]= urec->encryptedRandom[0]= random(); urec->random[1]= urec->encryptedRandom[1]= random(); KeySched(urec->u.record.Password, keyArray); temp = urec->encryptedRandom; Encode(keyArray, 8, &temp); } (*urec->usrCompletion) (urec); return; } /************************************************************************************ * GetAuthInfo ************************************************************************************/ Boolean GetAuthInfo(long *authInfo, unsigned long refNum) { long keyArray[32]; UserEntryPtr uEntry; char tempPassword[9]; char password[8]; if ((uEntry = findUserByRef(refNum)) == nil) return false; /* Use the entrey addrfess as the key to scramble the string. */ memset(tempPassword,0, 9); scramble (kPassWordKey, uEntry->password, tempPassword); BlockMove( &tempPassword[1], password, 8); KeySched(password, keyArray); Encode(keyArray, 8, &authInfo); return true; } /************************************************************************************ * random ************************************************************************************/ long random() { long randnum; GetDateTime((unsigned long *)&randnum); return randnum; } /************************************************************************************ * checkInit ************************************************************************************/ OSErr checkInit () { PPCGlobalParamsPtr ppcglobPtr; if (((ppcglobPtr = (PPCGlobalParamsPtr)getGlobal()) == nil) || !ppcglobPtr->inited) return notInitErr; else return 0; } /*--------------------------------------------------------------------------------------------------- ppcStartSecure is the entry point for StartSecureSession. ---------------------------------------------------------------------------------------------------*/ OSErr ppcStartSecure (StartSecureParam *pu) { Handle userName; short selectedItem, loginMethod; char password[10]; Str32 name; OSErr result; PPCGlobalParamsPtr ppcglobPtr; OSErr err; char *logTempStr; PPCStartPBPtr startPB; if (result = checkInit()) return result; ppcglobPtr = (PPCGlobalParamsPtr)getGlobal(); startPB = pu->startPb; if (!ValidPortName(startPB->portName)) { return(startPB->ioResult = badPortNameErr); } else if (startPB->serviceType != ppcServiceRealTime) { return(startPB->ioResult = badServiceMethodErr); } else if (!PortRefNumtoPtr(startPB->portRefNum, ppcglobPtr)) { return(startPB->ioResult = noPortErr); } else if (result = VerifyLocNameFormat(startPB->locationName)) { return(startPB->ioResult = result); } if(LocationNameLocal(pu->startPb->locationName)) { // user wants to make a local session startPB->userRefNum = 0; return PPCStart(startPB, false); } if ((pu->useDefault) && !(err = GetDefaultUser( &(pu->startPb)->userRefNum, name))) { /* Got the default user ref. Make the PPCStart call. Return if no error or if * the error code is not authFailErr, or userRejectErr. */ err = PPCStart(startPB, false); if (!err) { BlockMove(name, pu->userName,sizeof(Str32)); return noErr; } else if (err != userRejectErr && err != authFailErr) return err; } selectedItem = 7; loginMethod = 3; password [0] = 0; if (!(char)(*pu->userName)) { if (!(userName = GetResource('STR ', -16096))) { *pu->userName = 0; selectedItem = 5; } else { /* no need to lock userName for BlockMove */ BlockMove(*userName, pu->userName, 32); } } ppcglobPtr->ppcIcon = GetResource( 'ICN#', (short)-16411); MoveHHi ( ppcglobPtr->ppcIcon); HLock( ppcglobPtr->ppcIcon); if (pu->prompt && pu->prompt[0]) // If their exists a prompt, and of length > 0 then. logTempStr = pu->prompt; // user has supplied a string else { logTempStr = NULL; // Use default prompt, parameter text. ParamText(startPB->portName->name,startPB->locationName->u.nbpEntity.objStr,NULL,NULL); } result = PromptForUser (logTempStr, ppcglobPtr->ppcIcon, pu->userName, password, selectedItem, pu->allowGuest, &loginMethod, (ProcPtr)promptCallBack, startPB); HUnlock(ppcglobPtr->ppcIcon); if (result) return (startPB->ioResult = userCanceledErr); if (loginMethod == 1) *pu->guestSelected = 1; else *pu->guestSelected = 0; return (startPB->ioResult); } /*--------------------------------------------------------------------------------------------------- promptCallBack is called by the StartSecureSession dialog when it is time to start a secure session. ---------------------------------------------------------------------------------------------------*/ STATIC OSErr promptCallBack (StringPtr password, StringPtr name, short method, PPCStartPBPtr pb) { UserEntry *theUserEntry; OSErr err; Str255 message; short selectedItem; Boolean createdNewRefNum; createdNewRefNum = false; if (method == 1) // Do we attempt to log in as a guest? { pb->userRefNum = 0; err = PPCStart(pb,false); } else if (name[0]) // Use this else for CubeE { if (!(theUserEntry = FindUserByNameAndPassword(name,password))) { if (!(theUserEntry = GetFreeEntry ())) { pb->ioResult = noUserRefErr; return 0; } else // Create a new UserEntry { createdNewRefNum = true; scramble (kNameKey, name, theUserEntry->name); scramble (kPassWordKey, password, theUserEntry->password); } } pb->userRefNum = theUserEntry->ref; err = PPCStart(pb,false); if(err && createdNewRefNum) ppcDeleteUser(&theUserEntry->ref); } else // User didn't fill in the user name. goto GetName; if (err == authFailErr) { selectedItem = 7; GetIndString(message, ppcErrStrings, 1); } else if(err == userRejectErr || err == noUserNameErr) { GetName: selectedItem = 5; GetIndString(message, ppcErrStrings, 2); } else return(noErr); // This is not necessarly the error in the startPB. ParamText(message, nil, nil, nil); StopAlert( rPPCAlert, nil); ParamText(pb->portName->name,pb->locationName->u.nbpEntity.objStr,NULL,NULL); return selectedItem; } /*--------------------------------------------------------------------------------------------------- UserRefNumCompare is compares a userreference number and a UserEntry->ref to see if they are the same. ---------------------------------------------------------------------------------------------------*/ STATIC Boolean UserRefNumCompare(UserEntryPtr theUserEntry,unsigned long userRef) { return(theUserEntry->ref == userRef); } /*--------------------------------------------------------------------------------------------------- ppcDeleteUser, deletes a user by his reference number. It returns the result of the operation, either noErr or noUserRecErr. This is the entry point to DeleteUserIdentity. ---------------------------------------------------------------------------------------------------*/ OSErr ppcDeleteUser (unsigned long *userRef) { PPCGlobalParamsPtr ppcglobPtr; UserEntryPtr theUserEntry; ppcglobPtr = getGlobal(); if(theUserEntry = DeleteFromQueue(&ppcglobPtr->UserEntryQueue,*userRef,UserRefNumCompare)) { DisposeMem(theUserEntry); return(noErr); } else return(noUserRecErr); } /*--------------------------------------------------------------------------------------------------- ppcGetDefaultUser returns the user reference number, and the machine owners name if it alread exists. This is the entry point to GetDefaultUser. ---------------------------------------------------------------------------------------------------*/ OSErr ppcGetDefaultUser (GetDefaultParam *pu) { PPCGlobalParamsPtr ppcglobPtr; Handle userName; UserEntryPtr theUserEntry; OSErr result; char UserPassword[9]; if ((result = checkInit()) == noErr) { ppcglobPtr = getGlobal(); if (!(userName = GetResource('STR ', -16096))) { result = noDefaultUserErr; } else { { // The following block of code is just to get the password out. BTioParam btPb; // PB for B-Tree calls UserAcRecord theUserRecord; BTKey theKey; BldFSKey(0,*userName,(Ptr) &theKey); // make the key btPb.ioBTKeyPtr = (Ptr)&theKey; btPb.ioBuffer= (Ptr)&theUserRecord; // set pointer to record btPb.ioReqCount = sizeof (UserAcRecord); btPb.ioRefNum= ppcglobPtr->ugFile.refNum; btPb.ioBTHint1= 0; // zero Hint field result = BTSEARCHPPC(&btPb,false); // dispatch the call mixupPswd (theUserRecord.Password, theUserRecord.UserID, 0); // unscramble the password. BlockMove(theUserRecord.Password,UserPassword,8); // Copy the password. UserPassword[8] = 0; // Force Termination. c2pstr(UserPassword); // Convert it to a pascal string. } if (!(theUserEntry = FindUserByNameAndPassword(*userName,UserPassword))) { result = notLoggedInErr; } else { *pu->userRef = theUserEntry->ref; if(pu->userName) BlockMove(*userName,pu->userName,sizeof(Str32)); } } } return result; } /*--------------------------------------------------------------------------------------------------- FindUserByNameAndPassword will locate a user by his name and password. If NULL is passed as the password, the search is only done by name. ---------------------------------------------------------------------------------------------------*/ STATIC UserEntryPtr FindUserByNameAndPassword(StringPtr userName,StringPtr password) { PPCGlobalParamsPtr ppcglobPtr; UserEntryPtr theUserEntry; Str32 unscrambledName; char unscrambledpassword[9]; ppcglobPtr = getGlobal(); FOREACHELEMENT(theUserEntry,&ppcglobPtr->UserEntryQueue) { scramble(kNameKey,theUserEntry->name,unscrambledName); // unscramble the users name. if (EqualString(unscrambledName,userName,false,true)) { if(password) { scramble(kPassWordKey,theUserEntry->password,unscrambledpassword); // unscramble the password. if(!memcmp(unscrambledpassword,password,unscrambledpassword[0])) break; } else break; } } return(theUserEntry); } /*--------------------------------------------------------------------------------------------------- findUserByRef will locate a user by his reference number. ---------------------------------------------------------------------------------------------------*/ UserEntryPtr findUserByRef (unsigned long ref) { PPCGlobalParamsPtr ppcglobPtr; UserEntryPtr theUserEntry; ppcglobPtr = getGlobal(); FOREACHELEMENT(theUserEntry,&ppcglobPtr->UserEntryQueue) { if(theUserEntry->ref == ref) break; } return(theUserEntry); } /*--------------------------------------------------------------------------------------------------- GetFreeEntry allocates a new user entry ---------------------------------------------------------------------------------------------------*/ STATIC UserEntryPtr GetFreeEntry(void) { PPCGlobalParamsPtr ppcglobPtr; UserEntryPtr theUserEntry; ppcglobPtr = getGlobal(); if(theUserEntry = NewMem(sizeof(UserEntry))) { theUserEntry->ref = ppcglobPtr->nextUserRef++; if (ppcglobPtr->nextUserRef == 0) ++ppcglobPtr->nextUserRef; EnQueue(theUserEntry,&ppcglobPtr->UserEntryQueue); } return(theUserEntry); } /*--------------------------------------------------------------------------------------------------- Scramble is used to mutate a string in such a way as to be unrecognizable in memory. This procedure is not the most secure, but it should work just fine. ---------------------------------------------------------------------------------------------------*/ void scramble( unsigned char key, StringPtr pSrc, StringPtr pDest) { short len; if(pDest) // make sure it is not a null pointer. { len = *pSrc++; *pDest++ = len; while (len) { *pDest++ = *pSrc++ ^ key; --len; } } }