XNSAuthImpl.mesa
Demers, October 9, 1986 5:59:45 pm PDT
ToDo:
Finalization code for strong identities and conversations.
The daemon.
DIRECTORY
AuthenticationP14V2 USING [AuthenticationError, CallError, CallProblem, ChangeSimpleKey, ChangeStrongKey, CheckSimpleCredentials, CreateSimpleKey, CreateStrongKey, CredentialsPackage, DeleteSimpleKey, DeleteStrongKey, GetStrongCredentials, HashedPassword, Problem, SeqWords, SeqWordsObject, StrongCredentials, StrongVerifier, Which],
Basics USING [BITXOR, LongNumber, ShortNumber],
BasicTime USING [earliestGMT, FromNSTime, GetClockPulses, GMT, latestGMT, Now, Period, ToNSTime],
CrRPC USING [CreateClientHandle, DestroyClientHandle, Error, Handle, MarshalledROPEHWords],
DESFace USING [Block, Blocks, CBCCheckDecrypt, CBCCheckEncrypt, CorrectParity, EncryptBlock, Key, nullKey],
Process USING [Detach, Pause, SecondsToTicks],
ProcessorFace USING [processorID], -- there ought to be a better place to get this!
RefText USING [InlineAppendChar, ObtainScratch, ReleaseScratch],
Rope USING [Equal, FromRefText, InlineFetch, Length, ROPE],
TimeP15V2 USING [Time],
XNS USING [Address, Host, unknownHost, unknownSocket],
XNSAuth USING [Credentials, CredentialsType, HashedPassword, Key, Name, Verifier],
XNSRouter USING [GetHops, Hops],
XNSServerLocation USING [EachAddressProc, LocateServers],
XNSWKS USING [authenticationInfo]
;
XNSAuthImpl: CEDAR MONITOR
LOCKS lock USING lock: Lock
IMPORTS AuthenticationP14V2, Basics, BasicTime, CrRPC, DESFace, Process, ProcessorFace, RefText, Rope, XNSRouter, XNSServerLocation
EXPORTS XNSAuth
~ {
OPEN Auth: AuthenticationP14V2, Time: TimeP15V2;
authPgmNum: CARD ~ 14;
authVersionNum: CARDINAL ~ 2;
SeqWords: TYPE ~ Auth.SeqWords;
SeqWordsObject: TYPE ~ Auth.SeqWordsObject;
GMT: TYPE ~ BasicTime.GMT;
Block: TYPE ~ DESFace.Block;
ROPE: TYPE ~ Rope.ROPE;
NSTime: TYPE ~ Time.Time;
Credentials: TYPE ~ XNSAuth.Credentials;
CredentialsType: TYPE ~ XNSAuth.CredentialsType;
HashedPassword: TYPE ~ XNSAuth.HashedPassword;
Key: TYPE ~ XNSAuth.Key;
Name: TYPE ~ XNSAuth.Name;
Verifier: TYPE ~ XNSAuth.Verifier;
HostNumber: TYPE ~ XNS.Host;
authSvcName: Name ← ["CHServers", "CHServers", "Authentication Service"];
myHostNumber: HostNumber ← LOOPHOLE[ProcessorFace.processorID];
Lock: TYPE ~ REF LockObject;
LockObject: TYPE ~ MONITORED RECORD [];
AuthenticationError: PUBLIC ERROR [problem: Auth.Problem]
← Auth.AuthenticationError;
CallError: PUBLIC ERROR[problem: Auth.CallProblem, whichArg: Auth.Which]
← Auth.CallError;
MarshallError: ERROR ~ CODE;
CantContactAuthServer: PROC ~ {
ERROR };
AuthServerError: PROC [what: ROPE] ~ {
ERROR };
Initiator
Identities
SimpleIdentity: TYPE ~ REF SimpleIdentityObject;
SimpleIdentityObject: TYPE ~ RECORD [
name: Name,
password: ROPE,
credentials: Credentials,
verifier: Verifier
];
StrongIdentity: TYPE ~ REF StrongIdentityObject;
StrongIdentityObject: TYPE ~ RECORD [
next: StrongIdentity,
conversations: StrongConversation,
name: Name,
password: ROPE, -- optional, if present must agree with key
key: Key
];
Identity: TYPE ~ REF; -- SimpleIdentityObject * StrongIdentityObject
initiatorCacheLock: Lock ~ NEW[LockObject ← []];
strongIdentities: StrongIdentity ← NIL;
AddNewStrongIdentity: ENTRY PROC [lock: Lock ← initiatorCacheLock, newID: StrongIdentity] ~ {
newID.next ← strongIdentities;
strongIdentities ← newID };
RemoveOldStrongIdentity: ENTRY PROC [lock: Lock ← initiatorCacheLock, oldID: StrongIdentity] ~ {
p, prev: StrongIdentity;
p ← strongIdentities; prev ← NIL;
WHILE (p # NIL) AND (p # oldID) DO prev ← p; p ← p.next ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE strongIdentities ← p.next };
};
MakeIdentity: PUBLIC PROC[name: Name, password: ROPE,
credentialsType: CredentialsType ← strong, check: BOOLTRUE]
RETURNS [identity: Identity]
~ {
SELECT credentialsType FROM
simple => {
simpleID: SimpleIdentity;
simpleID ← NEW[SimpleIdentityObject ← [name~name, password~password, credentials~CredentialsFromName[name], verifier~VerifierFromHashedPassword[SimpleKeyFromPassword[password]]]];
identity ← simpleID;
IF check THEN CheckSimple[simpleID.credentials, simpleID.verifier];
};
strong => {
strongID: StrongIdentity ← NEW[StrongIdentityObject ← [name~name, password~password, key~StrongKeyFromPassword[password]]];
AddNewStrongIdentity[newID~strongID];
identity ← strongID;
IF check THEN Terminate[Initiate[identity, authSvcName]];
};
ENDCASE => ERROR;
};
MakeStrongIdentityUsingKey: PUBLIC PROC[name: Name, key: Key, check: BOOLTRUE]
RETURNS [identity: Identity]
~ {
strongID: StrongIdentity ← NEW[StrongIdentityObject ← [name~name, password~NIL, key~key]];
AddNewStrongIdentity[newID~strongID];
identity ← strongID;
IF check THEN Terminate[Initiate[identity, authSvcName]];
};
Conversations
SimpleConversation: TYPE ~ REF SimpleConversationObject;
SimpleConversationObject: TYPE ~ RECORD [
initiatorID: SimpleIdentity,
recipientName: Name
];
StrongConversation: TYPE ~ REF StrongConversationObject;
StrongConversationObject: TYPE ~ RECORD [
next: StrongConversation,
initiatorID: StrongIdentity,
timeStamp: GMT,
recipientName: Name,
recipientHostNumber: HostNumber,
credentials: Credentials,
conversationKey: Key,
lastStrongVerifier: Auth.StrongVerifier
];
Conversation: TYPE ~ REF; -- SimpleConversationObject * StrongConversationObject
Note there is no simple conversation cache on the initiator's side, since creating a simple conversation does not require contacting the authentication service.
StrongConversationCacheGet: ENTRY PROC[lock: Lock ← initiatorCacheLock,
id: StrongIdentity, name: Name] RETURNS[StrongConversation]
~ {
ENABLE UNWIND => NULL;
p, prev: StrongConversation;
prev ← NIL; p ← id.conversations;
WHILE (p # NIL) AND (NOT EqualNames[name, p.recipientName]) DO
prev ← p; p ← p.next;
ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE id.conversations ← p.next;
p.next ← NIL };
RETURN [p] };
StrongConversationCachePut: ENTRY PROC[lock: Lock ← initiatorCacheLock,
c: StrongConversation, id: StrongIdentity]
~ {
ENABLE UNWIND => NULL;
c.next ← id.conversations;
id.conversations ← c };
strongConversationCacheTimeout: INT ~ 300; -- ???? what's the right number ????
SweepStrongConversationCache: ENTRY PROC[lock: Lock ← initiatorCacheLock] ~ {
ENABLE UNWIND => NULL;
p, prev: StrongConversation;
now: GMT ~ BasicTime.Now[];
FOR strongID: StrongIdentity ← strongIdentities, strongID.next WHILE strongID # NIL DO
p ← strongID.conversations; prev ← NIL;
WHILE p # NIL DO
IF BasicTime.Period[from~p.timeStamp, to~now] < strongConversationCacheTimeout
THEN {
prev ← p;
p ← p.next }
ELSE {
p ← p.next;
IF prev = NIL THEN strongID.conversations ← p ELSE prev.next ← p };
ENDLOOP;
ENDLOOP;
};
Public Procedures
secondsBetweenRefreshes: CARDINAL ~ 300; -- ???? what's the right number ????
initialStrongVerifier: Auth.StrongVerifier ~ [BasicTime.ToNSTime[BasicTime.earliestGMT], 0];
Initiate: PUBLIC PROC [identity: Identity, recipientName: Name]
RETURNS [conversation: Conversation]
~ {
WITH identity SELECT FROM
simpleID: SimpleIdentity => {
c: SimpleConversation ← NEW[SimpleConversationObject ← [initiatorID~simpleID, recipientName~recipientName]];
conversation ← c };
strongID: StrongIdentity => {
c: StrongConversation;
c ← StrongConversationCacheGet[id~strongID, name~recipientName];
IF c = NIL THEN
c ← NEW[StrongConversationObject ← [timeStamp~BasicTime.earliestGMT, recipientName~recipientName, recipientHostNumber~XNS.unknownHost, credentials~[strong,NIL], conversationKey~, lastStrongVerifier~initialStrongVerifier]];
c.initiatorID ← strongID;
IF BasicTime.Period[from~c.timeStamp, to~BasicTime.Now[]] > secondsBetweenRefreshes THEN Refresh[c];
conversation ← c };
ENDCASE => ERROR;
};
Refresh: PUBLIC PROC [conversation: Conversation]
~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
NULL };
strongC: StrongConversation => { 
seqWords: SeqWords;
credentialsPackage: Auth.CredentialsPackage;
nonce: CARD ~ BasicTime.GetClockPulses[];
DoGetStrongCredentials: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
seqWords ← Auth.GetStrongCredentials[h, strongC.initiatorID.name, strongC.recipientName, nonce] };
CallRemote[DoGetStrongCredentials];
seqWords ← DecryptSeqWords[strongC.initiatorID.key, seqWords];
[cp~credentialsPackage] ← FetchCredentialsPackageFromSeqWords[seqWords, 0
! MarshallError => GOTO Bad];
IF credentialsPackage.nonce # nonce THEN GOTO Bad;
strongC.recipientName ← credentialsPackage.recipient;
strongC.credentials ← credentialsPackage.credentials;
strongC.conversationKey ← credentialsPackage.conversationKey;
strongC.lastStrongVerifier ← initialStrongVerifier;
strongC.timeStamp ← BasicTime.Now[];
EXITS
Bad => ERROR AuthenticationError[credentialsInvalid];
};
ENDCASE => ERROR;
};
Terminate: PUBLIC PROC [conversation: Conversation]
~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
NULL };
strongC: StrongConversation => { 
strongID: StrongIdentity ~ strongC.initiatorID;
strongC.initiatorID ← NIL;
StrongConversationCachePut[c~strongC, id~strongID] };
ENDCASE => ERROR;
};
GetCredentials: PUBLIC PROC [conversation: Conversation]
RETURNS [credentials: Credentials]
~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
credentials ← simpleC.initiatorID.credentials };
strongC: StrongConversation => { 
credentials ← strongC.credentials };
ENDCASE => ERROR;
};
SetRecipientHostNumber: PUBLIC PROC [conversation: Conversation,
recipientHostNumber: HostNumber]
~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
NULL };
strongC: StrongConversation => { 
strongC.recipientHostNumber ← recipientHostNumber };
ENDCASE => ERROR;
};
GetNextVerifier: PUBLIC PROC [conversation: Conversation]
RETURNS [verifier: Verifier]
~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
verifier ← simpleC.initiatorID.verifier };
strongC: StrongConversation => {
now: NSTime ~ BasicTime.ToNSTime[BasicTime.Now[]];
IF strongC.lastStrongVerifier.timeStamp = now
THEN strongC.lastStrongVerifier ← IncrementStrongVerifier[strongC.lastStrongVerifier]
ELSE strongC.lastStrongVerifier.ticks ← 0;
verifier ← EncryptedVerifierFromStrongVerifier[strongC.conversationKey, LOOPHOLE[strongC.recipientHostNumber], strongC.lastStrongVerifier];
};
ENDCASE => ERROR;
};
ReplyVerifierChecks: PUBLIC PROC [conversation: Conversation, verifier: Verifier]
RETURNS [ok: BOOL]
~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
ERROR AuthenticationError[inappropriateCredentials] };
strongC: StrongConversation => {
ENABLE MarshallError => { ok ← FALSE; CONTINUE };
sV: Auth.StrongVerifier ~ StrongVerifierFromEncryptedVerifier[ strongC.conversationKey, LOOPHOLE[strongC.recipientHostNumber], verifier];
ok ← (sV = IncrementStrongVerifier[strongC.lastStrongVerifier]);
IF ok THEN strongC.lastStrongVerifier ← sV };
ENDCASE => ERROR;
};
Recipient
Simple Conversations
RSimpleConversation: TYPE ~ REF RSimpleConversationObject;
RSimpleConversationObject: TYPE ~ RECORD [
next: RSimpleConversation,
referenced: BOOLTRUE,
initiatorName: Name,
credentials: Credentials,
verifier: Verifier
];
rSimpleConversationCacheLock: Lock ← NEW[LockObject ← []];
rSimpleConversationCache: RSimpleConversation ← NIL;
RSimpleConversationCacheGet: ENTRY PROC [
lock: Lock ← rSimpleConversationCacheLock,
credentials: Credentials] RETURNS [RSimpleConversation] ~ {
ENABLE UNWIND => NULL;
p, prev: RSimpleConversation;
IF credentials.type # simple THEN ERROR;
prev ← NIL; p ← rSimpleConversationCache;
WHILE (p # NIL) AND (NOT EqualSeqWords[credentials.value, p.credentials.value]) DO
prev ← p; p ← p.next;
ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE rSimpleConversationCache ← p.next;
p.next ← NIL;
p.referenced ← TRUE };
RETURN [p] };
RSimpleConversationCachePut: ENTRY PROC[
lock: Lock ← rSimpleConversationCacheLock,
conversation: RSimpleConversation] ~ {
ENABLE UNWIND => NULL;
conversation.next ← rSimpleConversationCache;
rSimpleConversationCache ← conversation };
SweepRSimpleConversationCache: ENTRY PROC[lock: Lock ← rSimpleConversationCacheLock] ~ {
ENABLE UNWIND => NULL;
p, prev: RSimpleConversation;
p ← rSimpleConversationCache; prev ← NIL;
WHILE p # NIL DO
IF p.referenced
THEN {
p.referenced ← FALSE;
prev ← p;
p ← p.next }
ELSE {
p ← p.next;
IF prev = NIL THEN rSimpleConversationCache ← p ELSE prev.next ← p };
ENDLOOP;
};
Strong Conversations
RStrongConversation: TYPE ~ REF RStrongConversationObject;
RStrongConversationObject: TYPE ~ RECORD [
next: RStrongConversation,
referenced: BOOLTRUE,
credentials: Credentials,
decryptedStrongCredentials: Auth.StrongCredentials,
lastStrongVerifier: Auth.StrongVerifier
];
rStrongConversationCacheLock: Lock ← NEW[LockObject ← []];
rStrongConversationCache: RStrongConversation ← NIL;
RStrongConversationCacheGet: ENTRY PROC [
lock: Lock ← rStrongConversationCacheLock,
credentials: Credentials] RETURNS [RStrongConversation] ~ {
ENABLE UNWIND => NULL;
p, prev: RStrongConversation;
IF credentials.type # strong THEN ERROR;
prev ← NIL; p ← rStrongConversationCache;
WHILE (p # NIL) AND (NOT EqualSeqWords[credentials.value, p.credentials.value]) DO
prev ← p; p ← p.next;
ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE rStrongConversationCache ← p.next;
p.next ← NIL;
p.referenced ← TRUE };
RETURN [p] };
RStrongConversationCachePut: ENTRY PROC[
lock: Lock ← rStrongConversationCacheLock,
conversation: RStrongConversation] ~ {
ENABLE UNWIND => NULL;
conversation.next ← rStrongConversationCache;
rStrongConversationCache ← conversation };
SweepRStrongConversationCache: ENTRY PROC[lock: Lock ← rStrongConversationCacheLock] ~ {
ENABLE UNWIND => NULL;
p, prev: RStrongConversation;
p ← rStrongConversationCache; prev ← NIL;
WHILE p # NIL DO
IF p.referenced
THEN {
p.referenced ← FALSE;
prev ← p;
p ← p.next }
ELSE {
p ← p.next;
IF prev = NIL THEN rStrongConversationCache ← p ELSE prev.next ← p };
ENDLOOP;
};
Public Procedures
Authenticate: PUBLIC PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier]
RETURNS [hisName: Name]
~ {
SELECT hisCredentials.type FROM
simple => {
hisName ← AuthenticateSimple[hisCredentials, hisVerifier] };
strong => {
[hisName~hisName] ← AuthenticateStrong[myIdentity~myIdentity, hisCredentials~hisCredentials, hisVerifier~hisVerifier, useExpiredCredentials~FALSE, computeReplyVerifier~FALSE] };
ENDCASE => ERROR;
};
AuthenticateWithExpiredCredentials: PUBLIC PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier]
RETURNS [hisName: Name] ~ {
SELECT hisCredentials.type FROM
simple => {
hisName ← AuthenticateSimple[hisCredentials, hisVerifier] };
strong => {
[hisName~hisName] ← AuthenticateStrong[myIdentity~myIdentity, hisCredentials~hisCredentials, hisVerifier~hisVerifier, useExpiredCredentials~TRUE, computeReplyVerifier~FALSE] };
ENDCASE => ERROR;
};
AuthenticateAndReply: PUBLIC PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier]
RETURNS [hisName: Name, replyVerifier: Verifier] ~ {
SELECT hisCredentials.type FROM
simple => {
ERROR AuthenticationError[inappropriateCredentials];
};
strong => {
[hisName, replyVerifier] ← AuthenticateStrong[myIdentity~myIdentity, hisCredentials~hisCredentials, hisVerifier~hisVerifier, useExpiredCredentials~FALSE, computeReplyVerifier~TRUE];
};
ENDCASE => ERROR;
};
Private Utilities
AuthenticateSimple: PROC [hisCredentials: Credentials, hisVerifier: Verifier]
RETURNS [hisName: Name]
~ {
rSimpleC: RSimpleConversation;
rSimpleC ← RSimpleConversationCacheGet[credentials~hisCredentials];
IF (rSimpleC = NIL) OR (NOT EqualSeqWords[hisVerifier, rSimpleC.verifier]) THEN {
CheckSimple[hisCredentials, hisVerifier];
rSimpleC ← NEW[RSimpleConversationObject ← [initiatorName~FetchNameFromSeqWords[hisCredentials.value, 0].name, credentials~hisCredentials, verifier~hisVerifier]];
};
hisName ← rSimpleC.initiatorName;
RSimpleConversationCachePut[conversation~rSimpleC];
};
verifierTimeout: INT ~ 60;
AuthenticateStrong: PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier,
useExpiredCredentials: BOOLFALSE, computeReplyVerifier: BOOLFALSE]
RETURNS [hisName: Name, replyVerifier: Verifier ← NIL] ~ {
rStrongC: RStrongConversation;
WITH myIdentity SELECT FROM
myStrongID: StrongIdentity => {
rStrongC ← RStrongConversationCacheGet[credentials~hisCredentials];
IF rStrongC = NIL THEN {
ENABLE MarshallError => CONTINUE;
temp: SeqWords ← DecryptSeqWords[key~myStrongID.key, seqWords~hisCredentials.value];
rStrongC ← NEW[RStrongConversationObject ← [credentials~hisCredentials, decryptedStrongCredentials~FetchStrongCredentialsFromSeqWords[temp,0].sc, lastStrongVerifier~initialStrongVerifier]];
};
IF rStrongC = NIL THEN ERROR AuthenticationError[credentialsInvalid];
IF NOT useExpiredCredentials THEN {
expired: BOOL ~ (BasicTime.Period[
from~BasicTime.Now[],
to~BasicTime.FromNSTime[rStrongC.decryptedStrongCredentials.expirationTime
]] >= 0);
IF expired THEN ERROR AuthenticationError[credentialsExpired] };
{ ENABLE MarshallError => GOTO Bad;
temp: Auth.StrongVerifier ← StrongVerifierFromEncryptedVerifier[
rStrongC.decryptedStrongCredentials.conversationKey, LOOPHOLE[myHostNumber], hisVerifier];
IF NOT StrongVerifierGT[new~temp, old~rStrongC.lastStrongVerifier] THEN ERROR AuthenticationError[verifierReused];
IF NOT useExpiredCredentials THEN {
IF StrongVerifierExpired[temp] THEN
ERROR AuthenticationError[verifierExpired];
};
IF computeReplyVerifier
THEN { rStrongC.lastStrongVerifier ← IncrementStrongVerifier[temp];
replyVerifier ← EncryptedVerifierFromStrongVerifier[
rStrongC.decryptedStrongCredentials.conversationKey, LOOPHOLE[myHostNumber], rStrongC.lastStrongVerifier] }
ELSE {
rStrongC.lastStrongVerifier ← temp };
EXITS
Bad => ERROR AuthenticationError[verifierInvalid];
};
hisName ← rStrongC.decryptedStrongCredentials.initiator;
RStrongConversationCachePut[conversation: rStrongC];
};
mySimpleID: SimpleIdentity => {
ERROR AuthenticationError[inappropriateCredentials] };
ENDCASE => ERROR;
};
CheckSimple: PROC[credentials: Credentials, verifier: Verifier] ~ {
Just return if okay, else raise CallError or AuthenticationError.
okay: BOOLFALSE;
DoCheckSimple: PROC [h: CrRPC.Handle, host: HostNumber]
~ { okay ← Auth.CheckSimpleCredentials[h, credentials, verifier] };
CallRemote[DoCheckSimple];
IF NOT okay -- can this happen? -- THEN
ERROR AuthenticationError[problem~credentialsInvalid];
};
Key and Password Administration
ChangeMyPasswords: PUBLIC PROC [identity: Identity, newPassword: ROPE,
changeStrong: BOOLTRUE, changeSimple: BOOLTRUE] ~ {
IF changeStrong THEN {
newKey: Key ← StrongKeyFromPassword[newPassword];
ChangeMyStrongKey[identity, newKey] };
IF changeSimple THEN {
newKey: HashedPassword ← SimpleKeyFromPassword[newPassword];
ChangeMySimpleKey[identity, newKey] };
};
CreateStrongKey: PUBLIC PROC [identity: Identity, name: Name, newKey: Key] ~ {
c: Conversation ← Initiate[identity, authSvcName];
DoIt: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.CreateStrongKey[h, GetCredentials[c], GetNextVerifier[c], name, newKey] };
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
ChangeMyStrongKey: PUBLIC PROC [identity: Identity, newKey: Key] ~ {
c: Conversation ← Initiate[identity, authSvcName];
DoIt: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.ChangeStrongKey[h, GetCredentials[c], GetNextVerifier[c], newKey] };
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
DeleteStrongKey: PUBLIC PROC [identity: Identity, name: Name] ~ {
c: Conversation ← Initiate[identity, authSvcName];
DoIt: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.DeleteStrongKey[h, GetCredentials[c], GetNextVerifier[c], name] };
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
CreateSimpleKey: PUBLIC PROC [identity: Identity, name: Name, newKey: HashedPassword] ~ {
c: Conversation ← Initiate[identity, authSvcName];
DoIt: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.CreateSimpleKey[h, GetCredentials[c], GetNextVerifier[c], name, newKey] };
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
ChangeMySimpleKey: PUBLIC PROC [identity: Identity, newKey: HashedPassword] ~ {
c: Conversation ← Initiate[identity, authSvcName];
DoIt: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.ChangeSimpleKey[h, GetCredentials[c], GetNextVerifier[c], newKey] };
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
DeleteSimpleKey: PUBLIC PROC [identity: Identity, name: Name] ~ {
c: Conversation ← Initiate[identity, authSvcName];
DoIt: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.DeleteSimpleKey[h, GetCredentials[c], GetNextVerifier[c], name] };
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
Public Utilities
StrongKeyFromPassword: PUBLIC PROC [password: ROPE] RETURNS [key: Key] ~ {
index: INT;
passwordLen: INT ~ Rope.Length[password];
ki: DESFace.Key ← DESFace.nullKey;
bi: Block;
biPlus1: Block;
FOR index ← 0, index + 4 WHILE index < passwordLen DO
len: INT ~ MIN[4, passwordLen-index];
TRUSTED {
StoreSubropeInBlock[password, index, len, @bi, TRUE];
DESFace.CorrectParity[@ki];
DESFace.EncryptBlock[key~ki, from~@bi, to~LOOPHOLE[LONG[@ki]]] };
DESFace.EncryptBlock[key~ki, from~@bi, to~@biPlus1];
ki ← LOOPHOLE[biPlus1] };
ENDLOOP;
TRUSTED { DESFace.CorrectParity[@ki] };
RETURN [ LOOPHOLE[ki] ];
};
GetRandomStrongKey: PUBLIC PROC RETURNS [key: Key] ~ {
ERROR };
SimpleKeyFromPassword: PUBLIC PROC [password: ROPE]
RETURNS [hashVal: HashedPassword ← 0] ~ {
c: CHAR;
acc: Basics.LongNumber;
FOR i: INT IN [0 .. Rope.Length[password]) DO
c ← Rope.InlineFetch[password, i];
SELECT c FROM
IN ['A .. 'Z] => c ← 'a + (c - 'A);
ENDCASE;
acc.hi ← hashVal; acc.lo ← ORD[c];
hashVal ← acc.lc MOD 65357;
ENDLOOP;
};
GetCredentialsType: PUBLIC PROC [credentials: Credentials]
RETURNS [CredentialsType] ~ {
RETURN [credentials.type] };
GetConversationDetails: PUBLIC PROC [conversation: Conversation] RETURNS [
recipientName: Name, recipientHostNumber: HostNumber, credentials: Credentials, conversationKey: Key, owner: Identity] ~ {
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
recipientName ← simpleC.recipientName;
recipientHostNumber ← XNS.unknownHost;
credentials ← simpleC.initiatorID.credentials;
conversationKey ← ALL[0];
owner ← simpleC.initiatorID };
strongC: StrongConversation => {
recipientName ← strongC.recipientName;
recipientHostNumber ← strongC.recipientHostNumber;
credentials ← strongC.credentials;
conversationKey ← strongC.conversationKey;
owner ← strongC.initiatorID };
ENDCASE => ERROR;
};
GetIdentityDetails: PUBLIC PROC [identity: Identity] RETURNS [
name: Name, password: ROPE, credentialsType: CredentialsType] ~ {
WITH identity SELECT FROM
simpleI: SimpleIdentity => {
name ← simpleI.name;
password ← simpleI.password;
credentialsType ← simple };
strongI: StrongIdentity => {
name ← strongI.name;
password ← strongI.password;
credentialsType ← strong };
ENDCASE => ERROR;
};
GetCredentialsDetails: PUBLIC PROC [myKey: Key, hisCredentials: Credentials]
RETURNS [ok: BOOL, credentialsType: CredentialsType, conversationKey: Key,
expirationTime: GMT, hisName: Name] ~ {
ok ← FALSE;
SELECT hisCredentials.type FROM
simple => {
ENABLE MarshallError => CONTINUE;
credentialsType ← simple;
conversationKey ← ALL[0];
expirationTime ← BasicTime.latestGMT;
hisName ← FetchNameFromSeqWords[hisCredentials.value, 0].name;
ok ← TRUE;
};
strong => {
ENABLE MarshallError => CONTINUE;
rStrongC: RStrongConversation;
sC: Auth.StrongCredentials;
credentialsType ← strong;
rStrongC ← RStrongConversationCacheGet[credentials~hisCredentials];
IF rStrongC # NIL
THEN {
sC ← rStrongC.decryptedStrongCredentials;
RStrongConversationCachePut[conversation: rStrongC] }
ELSE {
temp: SeqWords ← DecryptSeqWords[key~myKey, seqWords~hisCredentials.value];
[sc~sC] ← FetchStrongCredentialsFromSeqWords[temp,0] };
conversationKey ← sC.conversationKey;
expirationTime ← BasicTime.FromNSTime[sC.expirationTime];
hisName ← sC.initiator;
ok ← TRUE;
};
ENDCASE => ERROR;
};
Daemon and Finalization
secondsBetweenSweeps: CARDINAL ~ 300;
Daemon: PROC ~ {
DO
Process.Pause[Process.SecondsToTicks[secondsBetweenSweeps]];
SweepStrongConversationCache[];
SweepRStrongConversationCache[];
ENDLOOP;
};
Private Utilities
EqualNames: PROC [a: Name, b: Name] RETURNS [BOOL] ~ {
RETURN[Rope.Equal[a.organization, b.organization, FALSE]
AND Rope.Equal[a.domain, b.domain, FALSE]
AND Rope.Equal[a.object, b.object, FALSE]] };
EqualSeqWords: PROC [a, b: SeqWords] RETURNS [BOOL] ~ {
IF a.length # b.length THEN RETURN [FALSE];
FOR i: CARDINAL IN [0..a.length) DO
IF a.body[i] # b.body[i] THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE] };
IncrementStrongVerifier: PROC [v: Auth.StrongVerifier]
RETURNS [result: Auth.StrongVerifier] ~ {
result.timeStamp ← v.timeStamp;
IF (result.ticks ← v.ticks + 1) = 0
THEN result.timeStamp ← result.timeStamp + 1;
};
StrongVerifierGT: PROC [new, old: Auth.StrongVerifier]
RETURNS [gt: BOOL] ~ {
deltaT: INT ~ BasicTime.Period[from~BasicTime.FromNSTime[old.timeStamp], to~BasicTime.FromNSTime[new.timeStamp]];
SELECT deltaT FROM
> 0 => RETURN [TRUE];
< 0 => RETURN [FALSE];
0 => RETURN [new.ticks > old.ticks];
ENDCASE;
};
StrongVerifierExpired: PROC [v: Auth.StrongVerifier]
RETURNS [expired: BOOL] ~ {
deltaT: INT ~ BasicTime.Period[from~BasicTime.FromNSTime[v.timeStamp], to~BasicTime.Now[]];
RETURN [ deltaT > verifierTimeout ] };
FetchRopeFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, r: ROPE] ~ {
i, count, seqLen: CARDINAL;
w: Basics.ShortNumber;
buf: REF TEXT;
seqLen ← s.length;
IF where >= seqLen THEN ERROR MarshallError;
count ← s.body[where]; where ← where + 1;
buf ← RefText.ObtainScratch[count];
i ← 0;
WHILE i < count DO
IF where >= seqLen THEN ERROR MarshallError;
w.lc ← s.body[where]; where ← where + 1;
buf ← RefText.InlineAppendChar[buf, VAL[w.hi]]; i ← i + 1;
IF i < count THEN { buf ← RefText.InlineAppendChar[buf, VAL[w.lo]]; i ← i + 1 };
ENDLOOP;
newWhere ← where; r ← Rope.FromRefText[buf];
RefText.ReleaseScratch[buf] };
StoreRopeInSeqWords: PROC [r: ROPE, s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL] ~ {
i, length, seqLen: CARDINAL;
w: Basics.ShortNumber;
length ← Rope.Length[r];
seqLen ← s.length;
IF where >= seqLen THEN ERROR MarshallError;
s.body[where] ← length; where ← where + 1;
i ← 0;
WHILE i < length DO
w.hi ← ORD[Rope.InlineFetch[r,i]]; i ← i + 1;
w.lo ← (IF i < length THEN ORD[Rope.InlineFetch[r,i]] ELSE 0); i ← i + 1;
IF where >= seqLen THEN ERROR MarshallError;
s.body[where] ← LOOPHOLE[w]; where ← where + 1;
ENDLOOP;
RETURN [where] };
FetchSeqWordsFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, result: SeqWords] ~ {
n, seqLen: CARDINAL;
IF where >= seqLen THEN ERROR MarshallError;
n ← s.body[where]; where ← where + 1;
IF (n+where) >= seqLen THEN ERROR MarshallError;
result ← NEW[SeqWordsObject[n]];
FOR i: CARDINAL IN [0..n) DO
result.body[i] ← s.body[where]; where ← where + 1;
ENDLOOP;
newWhere ← where;
};
StoreSeqWordsInSeqWords: PROC [s: SeqWords, arg: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL] ~ {
n: CARDINAL ~ arg.length;
IF (where+n+1) >= s.length THEN ERROR MarshallError;
s.body[where] ← n; where ← where + 1;
FOR i: CARDINAL IN [0..n) DO
s.body[where] ← arg.body[i]; where ← where + 1;
ENDLOOP;
newWhere ← where;
};
FetchBlockFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, block: Block] ~ {
IF (newWhere ← where+4) >= s.length THEN ERROR MarshallError;
block[0] ← s.body[where];
block[1] ← s.body[where+1];
block[2] ← s.body[where+2];
block[3] ← s.body[where+3];
};
StoreBlockInSeqWords: PROC [s: SeqWords, block: Block, where: CARDINAL]
RETURNS [newWhere: CARDINAL] ~ {
IF (newWhere ← where+4) >= s.length THEN ERROR MarshallError;
s.body[where] ← block[0];
s.body[where+1] ← block[1];
s.body[where+2] ← block[2];
s.body[where+3] ← block[3];
};
FetchCredentialsFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, c: Credentials] ~ {
type: CARDINAL;
IF where >= s.length THEN ERROR MarshallError;
type ← s.body[where]; where ← where + 1;
SELECT type FROM
ORD[CredentialsType.strong] => c.type ← strong;
ORD[CredentialsType.simple] => c.type ← simple;
ENDCASE => ERROR MarshallError;
[newWhere~newWhere, result~c.value] ← FetchSeqWordsFromSeqWords[s, where];
};
FetchCARDFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, result: CARD] ~ {
n: Basics.LongNumber;
IF(newWhere ← where+2) >= s.length THEN ERROR MarshallError;
n.hi ← s.body[where];
n.lo ← s.body[where+1];
result ← n.lc };
FetchCredentialsPackageFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, cp: Auth.CredentialsPackage] ~ {
[where, cp.credentials] ← FetchCredentialsFromSeqWords[s, where];
IF cp.credentials.type # strong THEN ERROR AuthenticationError[inappropriateCredentials];
[where, cp.nonce] ← FetchCARDFromSeqWords[s, where];
[where, cp.recipient] ← FetchNameFromSeqWords[s, where];
[where, cp.conversationKey] ← FetchBlockFromSeqWords[s, where];
newWhere ← where };
FetchStrongCredentialsFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, sc: Auth.StrongCredentials] ~ {
[where, sc.conversationKey] ← FetchBlockFromSeqWords[s, where];
[where, sc.expirationTime] ← FetchCARDFromSeqWords[s, where];
[where, sc.initiator] ← FetchNameFromSeqWords[s, where];
newWhere ← where };
StoreSubropeInBlock: PROC [r: ROPE, index, len: INT,
to: LONG POINTER TO Block, mapCase: BOOLTRUE] ~ {
Store len chars in block using 16-bit encoding.
c: CHAR;
blockIndex: INT ← 0;
WHILE blockIndex < len DO
c ← Rope.InlineFetch[r, index];
IF mapCase THEN SELECT c FROM
IN ['A .. 'Z] => c ← 'a + (c - 'A);
ENDCASE;
TRUSTED { to[blockIndex] ← ORD[c] };
index ← index + 1;
blockIndex ← blockIndex + 1;
ENDLOOP;
WHILE blockIndex < 4 DO
TRUSTED { to[blockIndex] ← 0 };
blockIndex ← blockIndex + 1;
ENDLOOP;
};
FetchNameFromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, name: Name] ~ {
[where, name.organization] ← FetchRopeFromSeqWords[s, where];
[where, name.domain] ← FetchRopeFromSeqWords[s, where];
[where, name.object] ← FetchRopeFromSeqWords[s, where];
newWhere ← where };
StoreNameInSeqWords: PROC [name: Name, s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL] ~ {
where ← StoreRopeInSeqWords[name.organization, s, where];
where ← StoreRopeInSeqWords[name.domain, s, where];
where ← StoreRopeInSeqWords[name.object, s, where];
RETURN [where] };
MarshalledNameHWORDs: PROC [name: Name] RETURNS [hWords: CARDINAL] ~ {
RETURN [CrRPC.MarshalledROPEHWords[name.organization] + CrRPC.MarshalledROPEHWords[name.domain] + CrRPC.MarshalledROPEHWords[name.object]] };
SeqWordsFromName: PROC [name: Name] RETURNS [SeqWords] ~ {
s: SeqWords ← NEW[SeqWordsObject[MarshalledNameHWORDs[name]]];
[] ← StoreNameInSeqWords[name, s, 0];
RETURN [s] };
CredentialsFromName: PROC [name: Name]
RETURNS [credentials: Credentials] ~ {
RETURN[ [type~simple, value~SeqWordsFromName[name]] ] };
VerifierFromHashedPassword: PROC [hashedPassword: HashedPassword]
RETURNS [verifier: Verifier] ~ {
???? WORD SIZE DEPENDENT ????
verifier ← NEW[SeqWordsObject[1]];
verifier.body[0] ← hashedPassword };
EncryptSeqWords: PROC [key: Key, seqWords: SeqWords] RETURNS [result: SeqWords] ~ {
???? Word size dependent. Should use BYTES in here instead of SIZE ????
nBlocks: CARDINAL ← (seqWords.length + SIZE[Block] - 1) / SIZE[Block];
roundedLength: CARDINAL ← nBlocks * SIZE[Block];
result ← NEW[SeqWordsObject[roundedLength]];
IF roundedLength # seqWords.length THEN {
FOR i: CARDINAL IN [0..seqWords.length)
DO result.body[i] ← seqWords.body[i] ENDLOOP;
FOR i: CARDINAL IN [seqWords.length..roundedLength)
DO result.body[i] ← 0 ENDLOOP;
seqWords ← result };
TRUSTED {
blocksFrom: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
blocksTo: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[result, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
DESFace.CBCCheckEncrypt[LOOPHOLE[key], nBlocks, blocksFrom, blocksTo, ALL[0]];
};
};
DecryptSeqWords: PROC [key: Key, seqWords: SeqWords] RETURNS [result: SeqWords] ~ {
???? Word size dependent. Should use BYTES in here instead of SIZE ????
nBlocks: CARDINAL ~ seqWords.length / SIZE[Block];
roundedLength: CARDINAL ← nBlocks * SIZE[Block];
IF seqWords.length # roundedLength THEN ERROR MarshallError;
result ← NEW[SeqWordsObject[roundedLength]];
TRUSTED {
blocksFrom: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
blocksTo: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[result, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
DESFace.CBCCheckDecrypt[LOOPHOLE[key], nBlocks, blocksFrom, blocksTo, ALL[0]];
};
};
EncryptedVerifierFromStrongVerifier: PROC [conversationKey: Key,
hostNumber: MACHINE DEPENDENT RECORD [a, b, c: WORD],
strongVerifier: Auth.StrongVerifier] RETURNS [verifier: Verifier] ~ {
seqWords: SeqWords ~ NEW[SeqWordsObject[SIZE[Auth.StrongVerifier]]];
TRUSTED {
p: LONG POINTER TO Auth.StrongVerifier ← LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
p^ ← strongVerifier };
seqWords.body[0] ← Basics.BITXOR[seqWords.body[0], hostNumber.a];
seqWords.body[1] ← Basics.BITXOR[seqWords.body[0], hostNumber.b];
seqWords.body[2] ← Basics.BITXOR[seqWords.body[0], hostNumber.c];
verifier ← EncryptSeqWords[key~LOOPHOLE[conversationKey], seqWords~seqWords];
};
StrongVerifierFromEncryptedVerifier: PROC [conversationKey: Key,
hostNumber: MACHINE DEPENDENT RECORD [a, b, c: WORD],
verifier: Verifier] RETURNS [strongVerifier: Auth.StrongVerifier] ~ {
IF verifier.length # SIZE[Auth.StrongVerifier] THEN ERROR MarshallError;
verifier ← DecryptSeqWords[key~LOOPHOLE[conversationKey], seqWords~verifier];
verifier.body[0] ← Basics.BITXOR[verifier.body[0], hostNumber.a];
verifier.body[1] ← Basics.BITXOR[verifier.body[0], hostNumber.b];
verifier.body[2] ← Basics.BITXOR[verifier.body[0], hostNumber.c];
TRUSTED {
p: LONG POINTER TO Auth.StrongVerifier ← LOOPHOLE[
LOOPHOLE[verifier, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
strongVerifier ← p^ };
};
Locating Authentication Servers and Performing Remote Calls
ServerInfo: TYPE ~ REF ServerInfoObject;
ServerInfoObject: TYPE ~ RECORD [
next: ServerInfo,
address: XNS.Address,
hops: XNSRouter.Hops
];
serverInfoLock: Lock ← NEW[LockObject ← []];
serverBroadcastLock: Lock ← NEW[LockObject ← []];
serverInfo: ServerInfo ← NIL;
serverInfoTimestamp: GMT;
defaultServerHops: XNSRouter.Hops ~ 3;
maxServerHops: XNSRouter.Hops ~ 6;
secondsBetweenBroadcasts: INT ~ 180;
DeleteServer: ENTRY PROC [lock: Lock ← serverInfoLock, it: ServerInfo] ~ {
ENABLE UNWIND => NULL;
IF it = serverInfo THEN { serverInfo ← it.next; RETURN };
FOR p: ServerInfo ← serverInfo, p.next WHILE p # NIL DO
IF p.next = it THEN { p.next ← it.next; RETURN };
ENDLOOP;
};
AddServerAddress: ENTRY PROC [lock: Lock ← serverInfoLock, addr: XNS.Address, hops: XNSRouter.Hops] ~ {
ENABLE UNWIND => NULL;
p, prev, new: ServerInfo;
Delete old entry for this address (hops may have changed)
prev ← NIL; p ← serverInfo;
WHILE (p # NIL) AND (p.address # addr) DO { prev ← p; p ← p.next } ENDLOOP;
IF p = NIL
THEN {
new ← NEW[ServerInfoObject ← [next~NIL, address~addr, hops~hops]] }
ELSE {
IF prev # NIL THEN prev.next ← p.next ELSE serverInfo ← p.next;
new ← p; new.hops ← hops };
Add new entry in correct position (sorted by hops)
prev ← NIL; p ← serverInfo;
WHILE (p # NIL) AND (p.hops < hops) DO { prev ← p; p ← p.next } ENDLOOP;
new.next ← p;
IF prev # NIL THEN prev.next ← new ELSE serverInfo ← new;
};
BroadcastForServers: ENTRY PROC [lock: Lock ← serverBroadcastLock, maxHops: XNSRouter.Hops, firstResponseOnly: BOOL] ~ {
ENABLE UNWIND => NULL;
EachAddress: XNSServerLocation.EachAddressProc -- [addr: XNS.Address] -- ~ {
hops: XNSRouter.Hops;
addr.socket ← XNS.unknownSocket;
hops ← XNSRouter.GetHops[addr.net];
AddServerAddress[addr~addr, hops~hops] };
XNSServerLocation.LocateServers[socket~XNSWKS.authenticationInfo, remotePgm~authPgmNum, remotePgmVersion~authVersionNum, eachAddress~EachAddress, maxHops~maxHops, firstResponseOnly~firstResponseOnly];
serverInfoTimestamp ← BasicTime.Now[];
};
GetBestServer: PROC RETURNS [ServerInfo] ~ {
p: ServerInfo ← serverInfo; -- ATOMIC
SELECT TRUE FROM
(p = NIL) => {
BroadcastForServers[maxHops~maxServerHops, firstResponseOnly~TRUE];
p ← serverInfo;
TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~defaultServerHops, firstResponseOnly~FALSE] ] };
};
((p.hops > 0) AND (BasicTime.Period[from~serverInfoTimestamp, to~BasicTime.Now[]] > secondsBetweenBroadcasts)) =>
TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~MIN[(p.hops-1), defaultServerHops], firstResponseOnly~FALSE] ] };
ENDCASE;
RETURN [p];
};
CallRemote: PROC [proc: PROC [CrRPC.Handle, HostNumber]] ~ {
h: CrRPC.Handle ← NIL;
p: ServerInfo;
maxTries: CARDINAL ~ 10;
CleanUp: PROC ~ { IF h # NIL THEN { CrRPC.DestroyClientHandle[h]; h ← NIL }};
FOR try: CARDINAL IN [1..maxTries] DO
BEGIN
ENABLE {
UNWIND => CleanUp[];
CrRPC.Error => { CleanUp[]; DeleteServer[it~p]; LOOP }};
IF (p ← GetBestServer[]) = NIL THEN EXIT;
h ← CrRPC.CreateClientHandle[class~$SPP, remote~p.address];
proc[h, p.address.host]; CleanUp[]; RETURN;
END;
ENDLOOP;
CleanUp[]; CantContactAuthServer[];
};
[] ← GetBestServer[]; -- load the cache
TRUSTED { Process.Detach[ FORK Daemon[]] };
}.