DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, Digit, ESC, Letter, SP], Basics USING [BITXOR, RawWords], Booting USING [CheckpointProc, RegisterProcs, RollbackProc], ConvertUnsafe USING [AppendRope, ToRope], DefaultRemoteNames USING [Get], DESFace USING [Block, DecryptBlock, EncryptBlock, MakeKey], Disk USING [DriveAttributes], File USING [wordsPerPage], GermSwap USING [LP, mdsiGerm], GVBasics USING [maxRNameLength], GVNames USING [Authenticate], IO USING [EraseChar, GetChar, PutChar, PutRope, STREAM], PhysicalVolume USING [NextPhysical, Physical, PhysicalInfo, ReadCredentials, WriteCredentials], PrincOps USING [GFT, SD, sGFTLength], Process USING [Detach, InitializeMonitor, Pause, SecondsToTicks], Rope USING [Concat, Equal, Fetch, Find, FromChar, Length, ROPE, Substr], UserCredentials USING [CredentialsChangeProc, defaultOptions, LoginOptions, State], UserCredentialsBackdoor USING [GuestProcsRec], VM USING [AddressForPageNumber, Allocate, Free, Interval, PageCount, PagesForWords, SwapIn, WordsForPages]; UserCredentialsImpl: MONITOR IMPORTS Ascii, Basics, Booting, ConvertUnsafe, DESFace, DefaultRemoteNames, Disk, GermSwap, GVNames, IO, PhysicalVolume, Process, Rope, VM EXPORTS PhysicalVolume, UserCredentials, UserCredentialsBackdoor = BEGIN OPEN UserCredentials; ROPE: TYPE = Rope.ROPE; IllegalParameter: ERROR = CODE; Bug: ERROR = CODE; DiskNotReady: ERROR = CODE; BrokenDisk: ERROR = CODE; credentialsImplID: CARDINAL = 08312; obsoleteImplID: CARDINAL = credentialsImplID - 1; -- anything other than credentialsImplID decryptedID: DESFace.Block = ALL[credentialsImplID]; Credentials: TYPE = LONG POINTER TO CredentialsObject; CredentialsObject: PUBLIC TYPE = MACHINE DEPENDENT RECORD [ implementationID(0): CARDINAL _ credentialsImplID, checksum(1): CARDINAL _ 0, fill(2:0..13): CARDINAL[0..17777B] _ 0, kind(2:14..14+2+6*16-1): SELECT state(2:14..15): State FROM noCredentials => NULL, name => [ encryptedID(3): DESFace.Block _ NULL, userName(7): StringBody _ [length: 0, maxlength: NULL, text: NULL] ], nameHint, nameAndPassword => [ userName(3): StringBody _ [length: 0, maxlength: NULL, text: NULL] ], ENDCASE ]; bogusCredentials: CredentialsObject = [implementationID: obsoleteImplID, kind: noCredentials[]]; GermString: TYPE = LONG POINTER TO LONG STRING; germGFT: LONG POINTER = GermSwap.LP[PrincOps.GFT, GermSwap.mdsiGerm]; germSD: LONG POINTER TO Basics.RawWords = GermSwap.LP[PrincOps.SD, GermSwap.mdsiGerm]; gftLen: CARDINAL = germSD[PrincOps.sGFTLength]; nameInGerm: GermString = LOOPHOLE[germGFT+gftLen]; passwordInGerm: GermString = nameInGerm + SIZE[LONG STRING]; germFreeSpace: LONG POINTER = passwordInGerm + SIZE[LONG STRING]; germFreeSpaceCount: CARDINAL = VM.WordsForPages[VM.PagesForWords[gftLen]] - (gftLen + 2*SIZE[LONG STRING]); CredentialsChange: TYPE = RECORD [proc: CredentialsChangeProc, clientData: REF ANY]; CredentialsChangeList: TYPE = LIST OF CredentialsChange; credentials: Credentials _ NIL; credentialsBuffer: VM.Interval; credentialsPV: PhysicalVolume.Physical; procs: CredentialsChangeList _ NIL; in, out: IO.STREAM _ NIL; checkpointing: BOOL _ FALSE; rolledBack: CONDITION _ [timeout: 0]; checkpointName, checkpointPassword: ROPE _ NIL; IsGuestProcess: PROC [] RETURNS [isGuest: BOOL] _ FalseProc; GuestProcs: REF UserCredentialsBackdoor.GuestProcsRec _ NEW[UserCredentialsBackdoor.GuestProcsRec _ [FalseProc]] ; FalseProc: SAFE PROC RETURNS [isGuest: BOOL] = CHECKED { isGuest _ FALSE; }; RegisterGuestProcs: PUBLIC SAFE PROC [newProcs: REF UserCredentialsBackdoor.GuestProcsRec] = CHECKED { GuestProcs _ newProcs; IsGuestProcess _ newProcs.IsGuestProcess; }; Login: PUBLIC ENTRY SAFE PROC [ startInteraction: SAFE PROC RETURNS [in, out: IO.STREAM], endInteraction: SAFE PROC [in, out: IO.STREAM], options: LoginOptions _ defaultOptions] = TRUSTED { ENABLE UNWIND => NULL; dirty: BOOL _ FALSE; terminalOn: BOOL _ FALSE; oldName, oldPassword: ROPE; newName, newPassword: ROPE; EnsureTerminalOn: PROC = { IF ~terminalOn THEN {[in, out] _ startInteraction[]; terminalOn _ TRUE}; }; EnsureTerminalOff: PROC = { IF terminalOn THEN {endInteraction[in, out]; in _ out _ NIL; terminalOn _ FALSE}; }; HandleDiskNotReady: PROC = { EnsureTerminalOn[]; IO.PutRope[out, "Disk is not ready...will retry..."]; Process.Pause[Process.SecondsToTicks[5]]; IO.PutRope[out, "retrying\N"]; }; CleanupOnAbort: PROC = { IF options.ignoreDiskEntirely THEN FreeCredentials[] ELSE ReleaseDiskCredentials[FALSE]; SetCredentialsInGerm[oldName, oldPassword]; NotifyCredentialsChangeProcs[]; EnsureTerminalOff[]; }; EnsureInitialized[]; IF IsGuestProcess[] THEN GuestProcs.Login[startInteraction, endInteraction, options]; IF ~options.alwaysInteract AND nameInGerm^.length > 0 THEN RETURN; [oldName, oldPassword] _ GetInternal[]; AllocateCredentials[]; IF options.ignoreDiskEntirely THEN credentials^ _ bogusCredentials ELSE AcquireDiskCredentials[ ! DiskNotReady => {HandleDiskNotReady[]; RETRY}]; IF credentials.implementationID = credentialsImplID THEN { CheckCredentials: PROC [credentials: Credentials] RETURNS [ok: BOOL _ TRUE] = { Downgrade: PROC [state: State] = { [] _ ChangeStateInternal[state]; dirty _ TRUE; ok _ FALSE}; WITH cred: credentials SELECT FROM nameHint => { diskUserName: ROPE = ConvertUnsafe.ToRope[@cred.userName]; EnsureTerminalOn[]; SetCredentialsInGerm[diskUserName, NIL]; [] _ GetAndAuthenticate[ ! UNWIND => CleanupOnAbort[]]; IF (dirty _ ~diskUserName.Equal[ConvertUnsafe.ToRope[nameInGerm^]]) THEN [] _ ChangeStateInternal[nameHint]; }; nameAndPassword => { diskUserName: ROPE = ConvertUnsafe.ToRope[@cred.userName]; diskPassword: ROPE = ConvertUnsafe.ToRope[@cred.userName + SIZE[StringBody[diskUserName.Length[]]]]; SetCredentialsInGerm[diskUserName, diskPassword]; SELECT GVNames.Authenticate[diskUserName, diskPassword] FROM individual, allDown => NULL; notFound, group => Downgrade[nameHint]; badPwd => Downgrade[name]; ENDCASE => ERROR Bug; }; name => { EnsureTerminalOn[]; SetCredentialsInGerm[ConvertUnsafe.ToRope[@cred.userName], NIL]; DO -- loops only if GV down and password supplied doesn't work. block: DESFace.Block; SELECT GetAndAuthenticate[passwordOnly ! UNWIND => CleanupOnAbort[]] FROM ok => { DESFace.DecryptBlock[ key: DESFace.MakeKey[passwordInGerm^], from: @cred.encryptedID, to: @block]; IF (dirty _ dirty OR (block ~= decryptedID)) THEN [] _ ChangeStateInternal[name]; }; bogusGVName => Downgrade[nameHint]; gvDown => { DESFace.DecryptBlock[ key: DESFace.MakeKey[passwordInGerm^], from: @cred.encryptedID, to: @block]; IF block ~= decryptedID THEN {IO.PutRope[out, "Wrong credentials!\N"]; LOOP}; }; ENDCASE; EXIT ENDLOOP; }; ENDCASE => ERROR Bug; }; UNTIL CheckCredentials[credentials] DO ENDLOOP; } ELSE { ENABLE UNWIND => CleanupOnAbort[]; RewriteDisk: PROCEDURE RETURNS [rewrite: BOOLEAN] = INLINE { IF options.ignoreDiskEntirely THEN RETURN[FALSE]; IF ~options.confirmCredentialsOverwrite THEN RETURN[TRUE]; RETURN[~Confirm["Disk credentials missing or obsolete; shall I leave it that way? "]] }; EnsureTerminalOn[]; IF (dirty _ RewriteDisk[]) THEN [] _ ChangeStateInternal[ IF GetAndAuthenticate[] = ok AND ~options.prohibitDiskProtection AND Confirm["Do you wish to prevent others from accessing your disk? "] THEN name ELSE nameHint ] ELSE [] _ GetAndAuthenticate[]; }; IF options.ignoreDiskEntirely THEN FreeCredentials[] ELSE ReleaseDiskCredentials[dirty ! BrokenDisk => { EnsureTerminalOn[]; IO.PutRope[out, "Disk error rewriting credentials file...I give up\N"]; DO ENDLOOP; }; DiskNotReady => {HandleDiskNotReady[]; RETRY}; ]; EnsureTerminalOff[]; [newName, newPassword] _ GetInternal[]; IF ~(oldName.Equal[newName, FALSE] AND oldPassword.Equal[newPassword]) THEN NotifyCredentialsChangeProcs[]; }; GetState: PUBLIC ENTRY SAFE PROC RETURNS [state: State] = TRUSTED { ENABLE UNWIND => NULL; IF IsGuestProcess[] THEN RETURN GuestProcs.GetState[]; AcquireDiskCredentials[]; state _ credentials.state; ReleaseDiskCredentials[FALSE]; }; ChangeState: PUBLIC ENTRY SAFE PROC [new: State] RETURNS [old: State] = TRUSTED { ENABLE UNWIND => NULL; IF IsGuestProcess[] THEN RETURN GuestProcs.ChangeState[new]; AcquireDiskCredentials[]; old _ ChangeStateInternal[new]; ReleaseDiskCredentials[TRUE]; }; Get: PUBLIC ENTRY SAFE PROC RETURNS [name, password: ROPE] = TRUSTED { ENABLE UNWIND => NULL; EnsureInitialized[]; IF IsGuestProcess[] THEN RETURN GuestProcs.Get[]; [name, password] _ GetInternal[]; }; RegisterForChange: PUBLIC ENTRY SAFE PROC [proc: CredentialsChangeProc, clientData: REF ANY _ NIL] = TRUSTED { procs _ CONS[[proc, clientData], procs]; }; UnRegisterForChange: PUBLIC ENTRY SAFE PROC [proc: CredentialsChangeProc, clientData: REF ANY _ NIL] = TRUSTED { prev: CredentialsChangeList _ NIL; FOR cur: CredentialsChangeList _ procs, cur.rest UNTIL cur = NIL DO IF cur.first.proc = proc AND cur.first.clientData = clientData THEN { IF prev = NIL THEN procs _ cur.rest ELSE prev.rest _ cur.rest; EXIT }; prev _ cur; ENDLOOP; }; BreakIn: PROC = { BreakInInternal: ENTRY PROC = INLINE { AllocateCredentials[]; [] _ ChangeStateInternal[noCredentials]; ReleaseDiskCredentials[TRUE]; }; Process.InitializeMonitor[@LOCK]; BreakInInternal[]; }; EnsureInitialized: INTERNAL PROC = { WHILE checkpointing DO WAIT rolledBack; ENDLOOP; IF nameInGerm^ = NIL THEN SetCredentialsInGerm[NIL, NIL]; }; GetInternal: PROC RETURNS [name, password: ROPE] = TRUSTED { name _ ConvertUnsafe.ToRope[nameInGerm^]; password _ ConvertUnsafe.ToRope[passwordInGerm^]; }; SetCredentialsInGerm: PROC [name, password: ROPE] = { nameLen: NAT = name.Length[]; passwordLen: NAT = password.Length[]; IF SIZE[StringBody[nameLen]] + SIZE[StringBody[passwordLen]] > germFreeSpaceCount OR nameLen > GVBasics.maxRNameLength OR passwordLen > GVBasics.maxRNameLength THEN ERROR IllegalParameter; nameInGerm^ _ germFreeSpace; nameInGerm^^ _ StringBody[maxlength: nameLen, text: ]; ConvertUnsafe.AppendRope[to: nameInGerm^, from: name]; passwordInGerm^ _ nameInGerm^ + SIZE[StringBody[nameLen]]; passwordInGerm^^ _ StringBody[maxlength: passwordLen, text: ]; ConvertUnsafe.AppendRope[to: passwordInGerm^, from: password]; }; ChangeStateInternal: PROC [new: State] RETURNS [old: State] = { MoveString: PROC [new, old: LONG STRING] = { ConvertUnsafe.AppendRope[to: new, from: ConvertUnsafe.ToRope[old]]}; words: CARDINAL; old _ credentials.state; SELECT new FROM noCredentials => { credentials^ _ bogusCredentials; words _ SIZE[noCredentials CredentialsObject]; }; name => { credentials^ _ [kind: name[encryptedID: , userName: StringBody[maxlength: nameInGerm^.length, text: ]]]; WITH cred: credentials SELECT FROM name => { block: DESFace.Block _ decryptedID; DESFace.EncryptBlock[ key: DESFace.MakeKey[passwordInGerm^], from: @block, to: @cred.encryptedID]; MoveString[@cred.userName, nameInGerm^]; }; ENDCASE => ERROR Bug; words _ SIZE[name CredentialsObject] + (SIZE[StringBody[nameInGerm^.length]] - SIZE[StringBody[0]]); }; nameHint => { credentials^ _ [kind: nameHint[userName: StringBody[maxlength: nameInGerm^.length, text: ]]]; WITH cred: credentials SELECT FROM nameHint => MoveString[@cred.userName, nameInGerm^]; ENDCASE => ERROR Bug; words _ SIZE[nameHint CredentialsObject] + (SIZE[StringBody[nameInGerm^.length]] - SIZE[StringBody[0]]); }; nameAndPassword => { credentials^ _ [kind: nameAndPassword[userName: StringBody[maxlength: nameInGerm^.length, text: ]]]; WITH cred: credentials SELECT FROM nameAndPassword => { diskUserName: LONG STRING = @cred.userName; diskPassword: LONG STRING = @cred.userName + SIZE[StringBody[nameInGerm^.length]]; MoveString[@cred.userName, nameInGerm^]; diskPassword^ _ StringBody[maxlength: passwordInGerm^.length, text: ]; MoveString[diskPassword, passwordInGerm^]; }; ENDCASE => ERROR Bug; words _ SIZE[nameAndPassword CredentialsObject] + (SIZE[StringBody[nameInGerm^.length]] - SIZE[StringBody[0]]) + SIZE[StringBody[passwordInGerm^.length]]; }; ENDCASE => ERROR Bug; credentials.checksum _ ComputeChecksum[DESCRIPTOR[credentials, words]]; }; ComputeChecksum: PROC [input: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [checksum: WORD _ 0] = { FOR i: CARDINAL IN [0..input.LENGTH) DO checksum _ Basics.BITXOR[checksum, input[i]]; ENDLOOP; }; ValidChecksum: PROC [input: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [ok: BOOLEAN] = { checksum: WORD _ 0; FOR i: CARDINAL IN [0..input.LENGTH) DO checksum _ Basics.BITXOR[checksum, input[i]]; ENDLOOP; RETURN[checksum = 0] }; GetAndAuthenticate: PROC [askFor: {both, passwordOnly} _ both] RETURNS [outcome: {ok, bogusGVName, gvDown} _ ok] = { Rubout: ERROR = CODE; GetID: PROC [default: ROPE, echo: BOOL _ TRUE] RETURNS [id: ROPE] = { OPEN Ascii; firstTime: BOOLEAN _ TRUE; c: CHAR; EraseAll: PROC = { IF echo THEN FOR i: INT DECREASING IN [0..id.Length[]) DO out.EraseChar[id.Fetch[i]]; ENDLOOP; id _ NIL; }; Done: PROC [c: CHAR] RETURNS [BOOL] = INLINE { IF firstTime THEN { SELECT c FROM ControlA, BS, ControlQ, ControlW, ControlX, CR, SP => NULL; ENDCASE => EraseAll[]; firstTime _ FALSE; }; RETURN[c = SP OR c = CR] }; id _ default; IF echo THEN IO.PutRope[out, default]; c _ in.GetChar[]; UNTIL Done[c] DO SELECT c FROM DEL => ERROR Rubout; ControlA, BS => { len: INT _ id.Length[]; IF len > 0 THEN { len _ len - 1; IF echo THEN out.EraseChar[id.Fetch[len]]; id _ id.Substr[len: len]; }; }; ControlW, ControlQ => { alpha: BOOL _ FALSE; FOR i: INT DECREASING IN [0..id.Length[]) DO ch: CHAR = id.Fetch[i]; IF Ascii.Letter[ch] OR Ascii.Digit[ch] THEN alpha _ TRUE ELSE IF alpha THEN {id _ id.Substr[len: i + 1]; EXIT}; IF echo THEN out.EraseChar[ch]; REPEAT FINISHED => id _ NIL; ENDLOOP; }; ControlX => EraseAll[]; ENDCASE => {id _ id.Concat[Rope.FromChar[c]]; IF echo THEN out.PutChar[c]}; c _ in.GetChar[]; ENDLOOP; }; name, password: ROPE _ NIL; cancelString: ROPE = " XXX\N"; DO ENABLE UNWIND => IO.PutRope[out, cancelString]; name _ ConvertUnsafe.ToRope[nameInGerm^]; IO.PutRope[out, "Name: "]; IF askFor = passwordOnly THEN IO.PutRope[out, name] ELSE { name _ GetID[name ! Rubout => {IO.PutRope[out, cancelString]; LOOP}]; IF name.Find["."] = -1 THEN { defaultRegistry: ROPE = Rope.Concat[".", DefaultRemoteNames.Get[].registry]; IO.PutRope[out, defaultRegistry]; name _ name.Concat[defaultRegistry]}; }; IO.PutRope[out, " password: "]; password _ GetID[NIL, FALSE ! Rubout => {IO.PutRope[out, cancelString]; LOOP}]; SetCredentialsInGerm[name, password]; IO.PutRope[out, " GV..."]; SELECT GVNames.Authenticate[name, password] FROM individual => {IO.PutRope[out, "ok\N"]; EXIT}; notFound, group => { IO.PutRope[out, "invalid user name"]; IF askFor = passwordOnly THEN {out.PutChar['\N]; outcome _ bogusGVName; EXIT}; }; badPwd => IO.PutRope[out, "incorrect password"]; allDown => { IO.PutRope[out, "Grapevine down, proceeding anyway\N"]; outcome _ gvDown; EXIT}; ENDCASE; IO.PutRope[out, ", try again.\N"]; ENDLOOP; }; Confirm: PROCEDURE [message: ROPE _ NIL] RETURNS [BOOL] = { DO IF message ~= NIL THEN IO.PutRope[out, message]; SELECT in.GetChar[ ! UNWIND => IO.PutRope[out, " XXX\N"]] FROM 'y, 'Y, Ascii.SP, Ascii.CR, Ascii.ESC => {IO.PutRope[out, "Yes\N"]; RETURN[TRUE]}; 'n, 'N, Ascii.DEL => {IO.PutRope[out, "No\N"]; RETURN[FALSE]}; ENDCASE; ENDLOOP}; NotifyCredentialsChangeProcs: PROC = { Process.Detach[FORK NotifyProcess[CopyCredentialsChangeList[procs]]]; }; NotifyProcess: PROC [list: CredentialsChangeList] = { WHILE list # NIL DO list.first.proc[list.first.clientData]; list _ list.rest; ENDLOOP; }; CopyCredentialsChangeList: PROC [list: CredentialsChangeList] RETURNS [head: CredentialsChangeList _ NIL] = { IF list # NIL THEN { tail: CredentialsChangeList _ head _ LIST[list.first]; DO list _ list.rest; IF list = NIL THEN EXIT; tail _ tail.rest _ LIST[list.first]; ENDLOOP; }; }; AllocateCredentials: PROC = { diskCredentialsPages: VM.PageCount = VM.PagesForWords[File.wordsPerPage]; credentialsBuffer _ VM.Allocate[diskCredentialsPages]; credentials _ VM.AddressForPageNumber[credentialsBuffer.page]; VM.SwapIn[interval: credentialsBuffer, kill: TRUE, pin: TRUE]; }; FreeCredentials: PROC = { VM.Free[credentialsBuffer]; credentials _ NIL; }; AcquireDiskCredentials: PROC = { IF credentialsPV = NIL THEN GetCredentialsVolume[]; IF credentials = NIL THEN AllocateCredentials[]; SELECT PhysicalVolume.ReadCredentials[credentialsPV, credentials] FROM ok => { words: CARDINAL; WITH cred: credentials SELECT FROM noCredentials => words _ SIZE[noCredentials CredentialsObject]; name => words _ SIZE[name CredentialsObject] + (SIZE[StringBody[cred.userName.length]] - SIZE[StringBody[0]]); nameHint => words _ SIZE[nameHint CredentialsObject] + (SIZE[StringBody[cred.userName.length]] - SIZE[StringBody[0]]); nameAndPassword => { diskUserName: LONG STRING = @cred.userName; diskPassword: LONG STRING = @cred.userName + SIZE[StringBody[diskUserName.length]]; words _ SIZE[nameAndPassword CredentialsObject] + (SIZE[StringBody[diskUserName.length]] - SIZE[StringBody[0]]) + SIZE[StringBody[diskPassword.length]]; }; ENDCASE; IF ~ValidChecksum[DESCRIPTOR[credentials, words]] THEN credentials^ _ bogusCredentials; }; hardware, software => credentials^ _ bogusCredentials; wentOffline => ERROR DiskNotReady; ENDCASE => ERROR Bug; }; ReleaseDiskCredentials: PROC [dirty: BOOL] = { IF dirty THEN { IF credentialsPV = NIL THEN GetCredentialsVolume[]; SELECT PhysicalVolume.WriteCredentials[credentialsPV, credentials] FROM ok => NULL; hardware => ERROR BrokenDisk; wentOffline => ERROR DiskNotReady; ENDCASE => ERROR Bug; }; FreeCredentials[]; }; GetCredentialsVolume: PROC = { UNTIL (credentialsPV _ PhysicalVolume.NextPhysical[credentialsPV]) = NIL OR Disk.DriveAttributes[PhysicalVolume.PhysicalInfo[credentialsPV].channel].ordinal = 0 DO ENDLOOP; }; Checkpointing: ENTRY Booting.CheckpointProc = TRUSTED { EnsureInitialized[]; checkpointing _ TRUE; [checkpointName, checkpointPassword] _ GetInternal[]; }; RollingBack: ENTRY Booting.RollbackProc = TRUSTED { name, password: ROPE; IF ~checkpointing THEN ERROR; checkpointing _ FALSE; EnsureInitialized[]; [name, password] _ GetInternal[]; IF ~(name.Equal[checkpointName, FALSE] AND password.Equal[checkpointPassword]) THEN NotifyCredentialsChangeProcs[]; BROADCAST rolledBack; }; Initialize: ENTRY PROC = { EnsureInitialized[]; Booting.RegisterProcs[c: Checkpointing, r: RollingBack]; }; Initialize[]; END. FUserCredentialsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Levin on September 23, 1983 5:44 pm Birrell, December 13, 1983 3:48 pm Bob Hagmann May 3, 1985 11:24:25 am PDT Russ Atkinson (RRA) May 13, 1985 10:05:55 pm PDT Errors Declarations for data structures on disk The text portion of the userName follows here The text portion of the userName follows here If state = nameAndPassword, another StringBody follows Declarations for data structures in memory (germ) The credentials are stored in the tail of the germ's MDS, just after the GFT. We can only rely on enough space to fill out the pages containing the germ's GFT. Global Variables (protected by the monitor) Default IsGuestProcess Exported to UserCredentialsBackdoor Exports to UserCredentials already logged in Grapevine is up, and the credentials are valid. The password used to encrypt the information in the credentials file may not be the same one that was used (successfully) to authenticate the user to Grapevine. After all, the user may have changed his password in the Grapevine database since this procedure was last called. However, since the user name presented to Grapevine was forced to come from the disk and Grapevine authenticated the user, we can be confident that this is the same user who requested that the encrypted information be stored on the disk originally. We therefore encrypt with the new password and rewrite the credentials file. In effect, this gives us a cache of the last successful Grapevine authentication, which will be useful if Grapevine proves to be down on a subsequent Login. Grapevine is down, so we must fall back on our cached information on the disk. We use the password supplied by the user to decrypt then compare it with the expected value. If it matches, the user is assumed to be authentic, and the password he supplied is highly likely to be the one he last successfully used to authenticate himself with Grapevine. The internal signals from AcquireDiskCredentials and ReleaseDiskCredentials are presently uncaught, since we don't have any better way of dealing with them. *** For emergency use only *** Internal procedures Note: one might think that this could be done at module start time, rather than on every call to a public procedure of this module. However, the contents of the germ mds can change after this module is started (e.g., a checkpoint is taken, then a physical boot causes a rollback). text to be backed up is of the form ..., the and following are to be removed. Registration stuff RRA: fork the notifier process so we can call to Get to obtain the credentials. After all, it does seem natural to use the user name when noticing that the credentials have changed, doesn't it? Copy the list to allow changes to occur without disturbing our enumeration. Disk access PhysicalVolume assumes that the credentials occupy a maximum of File.wordsPerPage words. We assert that: File.wordsPerPage >= SIZE[nameAndPassword CredentialsObject] - SIZE[StringBody[0]] + 2*SIZE[StringBody[GVBasics.maxRNameLength]]]; The disk is there, but it doesn't seem to work, treat as "no credentials" now. Start code Bob Hagmann March 18, 1985 9:16:47 am PST changes to: DIRECTORY, Login, GetState, ChangeState, Get Bob Hagmann May 3, 1985 11:24:25 am PDT changes to: UserCredentialsImpl Ê®˜codešœ™Kšœ Ïmœ1™˜SKšœžœ˜.Kšžœžœc˜kK˜—šœž˜Kšžœ^žœ!ž˜ŠKšžœ;˜BKšž œ˜—˜Kšžœžœžœ˜K˜—šœ™K˜Kšœžœžœ˜Kšœžœžœ˜Kšœžœžœ˜Kšœ žœžœ˜—K˜šœ(™(K˜Kšœžœ ˜$KšœžœÏc(˜[K˜Kšœžœ˜4K˜Kš œ žœžœžœžœ˜6š œž œžœž œžœ˜;Kšœžœ˜2Kšœ žœ˜Kšœžœ˜'šœžœž˜;Kšœžœ˜˜ Kšœ žœ˜%Kšœ1žœžœ˜BKšœ-™-K˜—˜Kšœ1žœžœ˜BKšœ-™-Kšœ6™6K˜—Kšž˜—K˜K˜—K˜`K™—šœ1™1K˜Kšœ5žœžœPžœ™ K˜Kš œ žœžœžœžœžœžœ˜/K˜Kš œ žœžœ žœ žœ˜EKš œžœžœžœžœ žœ˜VKšœžœ˜/Kšœž œ˜2Kšœ*žœžœžœ˜Kšž˜K˜—Kšœ ˜ Kšžœ˜—K˜K™——šœ™K˜š¡œžœ˜š¡œžœžœžœ˜&K˜K˜(Kšœžœ˜K˜—Kšœžœ˜!K˜K˜K˜——šœ™K˜š¡œžœžœ˜$Kšœ™™™Kšžœžœžœ žœ˜0Kš žœžœžœžœžœ˜9Kšœ˜K˜—š¡ œž œžœžœ˜˜>K˜>K˜K˜—š¡œžœ žœ˜?š¡ œžœ žœžœ˜,KšœD˜D—Kšœžœ˜K˜šžœž˜˜K˜ Kšœžœ"˜.K˜—˜ ˜K˜Y—šžœžœž˜"˜ K˜#˜K˜&K˜%—K˜(K˜—Kšžœžœ˜—šœžœ˜&Kšœžœ#žœ˜=—K˜—˜ ˜K˜N—šžœžœž˜"K˜4Kšžœžœ˜—šœžœ˜*Kšœžœ#žœ˜=—K˜—˜˜K˜U—šžœžœž˜"˜Kšœžœžœ˜+Kšœžœžœžœ!˜RK˜(K˜FK˜*K˜—Kšžœžœ˜—šœžœ%˜1Kšœžœ#žœ˜>Kšžœ%˜)—K˜—Kšžœžœ˜—Kšœ'ž œ˜GK˜K˜—š¡œžœ žœž œžœžœžœžœ˜@Kšžœ žœ ˜ š žœžœžœ žœž˜'Kšœžœ˜-Kšžœ˜—K˜K˜—š¡ œžœ žœž œžœžœžœžœ˜>Kšžœžœ˜Kšœ žœ˜š žœžœžœ žœž˜'Kšœžœ˜-Kšžœ˜—Kšžœ˜K˜K˜—š¡œžœ&˜>Kšžœ.˜5Kšœžœžœ˜š¡œžœ žœžœžœžœžœ˜EKšžœ˜ Kšœ žœžœ˜Kšœžœ˜š¡œžœ˜šžœž˜ Kš žœžœž œžœžœžœ˜Q—Kšœžœ˜ K˜—š ¡œžœžœžœžœžœ˜.šžœ žœ˜šžœž˜ Kš œ žœ žœžœžœ˜;Kšžœ˜—Kšœ žœ˜Kšœ˜—Kšžœžœžœžœ˜K˜—K˜ Kšžœžœžœ˜&Kšœ˜šžœ ž˜šžœž˜ Kšžœžœ˜šœ žœ˜Kšœžœ˜šžœ žœ˜K˜Kšžœžœ˜*Kšœ˜Kšœ˜—K˜—šœ˜Kšœ~™~Kšœžœžœ˜š žœžœž œžœž˜,Kšœžœ˜Kšžœžœžœ ž˜8Kšžœžœžœžœ˜6Kšžœžœ˜šž˜Kšžœ žœ˜—Kšžœ˜—K˜—KšœŸ˜Kšžœ'žœžœ˜K—K˜Kšžœ˜—Kšœ˜—Kšœžœžœ˜Kšœžœ ˜šž˜Kšžœžœžœ˜/Kšœ)˜)Kšžœ˜šžœ˜Kšžœžœ˜šžœ˜Kšœžœžœ˜Ešžœžœ˜Kšœžœ7˜LKšžœE˜G—K˜——Kšžœ˜ Kš œžœžœžœžœ˜OK˜%Kšžœ˜šžœ&ž˜0Kšœžœžœ˜.˜Kšžœ#˜%Kšžœžœ+žœ˜NK˜—Kšœ žœ$˜0šœ ˜ KšžœHžœ˜P—Kšžœ˜—Kšžœ ˜"Kšžœ˜—K˜K˜—š ¡œž œ žœžœžœžœ˜;šž˜Kšžœ žœžœžœ˜0šžœžœžœž˜>Kš œžœžœžœžœžœžœ˜RKš œžœžœžœžœ˜>Kšžœ˜—Kšžœ˜ K˜———™K™š¡œžœ˜&KšžœŒ™Kšœžœ2˜EK˜K˜—š¡ œžœ"˜5šžœžœž˜K˜'K˜Kšžœ˜—K˜K˜—š¡œžœžœ žœ˜mšžœžœžœ˜Kšœ%žœ ˜6šž˜K˜Kšžœžœžœžœ˜Kšœžœ ˜$Kšžœ˜—K˜—K˜K˜——šœ ™ K™š¡œžœ˜šœžœ žœ"˜Išœi™išœT™TKšœ-™-———Kšœžœ ˜6Kšœžœ.˜>Kšžœ+žœžœ˜>K˜K˜—š¡œžœ˜Kšžœ˜Kšœžœ˜K˜K˜—š¡œžœ˜ Kšžœžœžœ˜3Kšžœžœžœ˜0šžœ<ž˜F˜Kšœžœ˜šžœžœž˜"Kšœžœ"˜?˜šœžœ˜&Kšœžœ%žœ˜?——˜ šœžœ˜*Kšœžœ%žœ˜?——˜Kšœžœžœ˜+Kšœžœžœžœ"˜Sšœžœ%˜1Kšœžœ$žœ˜?Kšžœ"˜&—K˜—Kšžœ˜—šžœž œž˜6Kšœ ˜ —K˜—šœ˜KšœN™NK˜ —Kšœžœ˜"Kšžœžœ˜—K˜K˜—š¡œžœ žœ˜.šžœžœ˜Kšžœžœžœ˜3šžœ=ž˜GKšœžœ˜ Kšœ žœ ˜Kšœžœ˜"Kšžœžœ˜—K˜—K˜K˜K˜—š¡œžœ˜šžœ@žœž˜KKšœUž˜WKšžœ˜—K˜K˜—šœžœžœ˜7Kšœ˜Kšœžœ˜K˜5K˜K˜—šœ žœžœ˜3Kšœžœ˜Kšžœžœžœ˜Kšœžœ˜Kšœ˜K˜!šžœžœžœ%ž˜SK˜—Kšž œ ˜K˜K˜—Kšœ ™ K˜š¡ œžœžœ˜Kšœ˜K˜8Kšœ˜—K˜K˜ K˜—Kšžœ˜K˜™)Kšœ Ïr,™8—K™™'Kšœ ¢™—K™—…—GŽl‚