SunAuthUnixImpl.mesa
Demers, October 7, 1987 4:11:58 pm PDT
DIRECTORY
Ascii USING [Lower],
BasicTime USING [GetClockPulses],
Convert USING [CardFromRope, Error],
RefText USING [New, TrustTextAsRope],
Rope USING [Equal, FromRefText, ROPE, ToRefText],
SunAuthUnix USING [Groups, GroupsObject],
SunRPC USING [CloseReader, CloseWriter, Error, GetCard32, GetRope, Handle, OpenReader, OpenWriter, PutCard32, PutRope],
SunRPCAuth USING [AuthenticateProc, CheckReplyVerifierProc, Conversation, ConversationObject, Error, GetCredentialsAndNextVerifierProc, InitiateProc, NoShortcutProc, nullFlavor, Procs, ProcsObject, Register, shortFlavor, SweepProc, TerminateProc, unixFlavor],
SunYPAgent USING [Error, First, Handle, Match, Next, ObtainHandle, ReleaseHandle, TextSeq, TokenizeUsingSeparator],
ThisMachine USING [Name]
;
SunAuthUnixImpl: CEDAR MONITOR
LOCKS lock USING lock: Lock
IMPORTS Ascii, BasicTime, Convert, RefText, Rope, SunRPC, SunRPCAuth, SunYPAgent, ThisMachine
EXPORTS SunAuthUnix
~ {
Types
ROPE: TYPE ~ Rope.ROPE;
Conversation: TYPE ~ SunRPCAuth.Conversation;
ConversationObject: TYPE ~ SunRPCAuth.ConversationObject;
Lock: TYPE ~ REF LockObject;
LockObject: TYPE ~ MONITORED RECORD [];
Parameters
initialTTL: CARDINAL ← 3600;
passwordMapName: ROPE ← "passwd.byname";
groupMapName: ROPE ← "group.byname";
passwordPos: CARDINAL ~ 1;
uidPos: CARDINAL ~ 2;
gidPos: CARDINAL ~ 3;
grpNamePos: CARDINAL ~ 0;
grpNumPos: CARDINAL ~ 2;
grpMembersPos: CARDINAL ~ 3;
maxGroups: CARDINAL ~ 10;
maxCredentialsBytes: CARDINAL ← 350;
maxUnixNameLen: CARDINAL ← 8;
Conversation Cache
ConversationData: TYPE ~ REF ConversationDataObject;
ConversationDataObject: TYPE ~ RECORD [
next: ConversationData,
conversation: Conversation,
ttl: CARDINAL ← initialTTL,
name, password: ROPE,
credentials: REF TEXT,
shortCredentials: REF TEXT
];
conversationDataCache: ConversationData ← NIL;
conversationCacheLock: Lock ← NEW[LockObject];
GetConversationFromCache: ENTRY PROC [name, password: ROPE, lock: Lock ← conversationCacheLock] RETURNS [c: Conversation] ~ {
p, prev: ConversationData;
p ← conversationDataCache;
WHILE p # NIL DO
IF Rope.Equal[p.name, name] AND Rope.Equal[p.password, password] THEN EXIT;
prev ← p; p ← p.next;
ENDLOOP;
IF p = NIL THEN RETURN [NIL];
IF prev = NIL THEN conversationDataCache ← p.next ELSE prev.next ← p.next;
RETURN [p.conversation];
};
PutConversationIntoCache: ENTRY PROC [c: Conversation, lock: Lock ← conversationCacheLock] ~ {
cD: ConversationData ~ NARROW[c.conversationData];
cD.next ← conversationDataCache;
conversationDataCache ← cD;
};
Sweep: SunRPCAuth.SweepProc -- [registrationData, secondsSinceLastSweep] -- ~ {
SweepInner[secondsSinceLastSweep];
};
SweepInner: ENTRY PROC [secs: CARD, lock: Lock ← conversationCacheLock] ~ {
p, prev: ConversationData;
p ← conversationDataCache;
WHILE p # NIL DO
IF p.ttl > secs
THEN {
p.ttl ← p.ttl - secs;
prev ← p;
}
ELSE {
IF prev = NIL THEN conversationDataCache ← p.next ELSE prev.next ← p.next;
};
p ← p.next;
ENDLOOP;
};
Unix Utilities
UnixPasswordMatches: PROC [uid: CARD, name, password, encryptedPassword: ROPE]
RETURNS [matches: BOOL] ~ {
RETURN [TRUE];
};
FixNameForUnix: PUBLIC PROC [name: ROPE] RETURNS [fixedName: ROPE] ~ {
buffer: REF TEXT ← Rope.ToRefText[name];
i: CARDINAL ← 0;
len: CARDINALMIN[buffer.length, maxUnixNameLen];
DO
c: CHAR;
IF (i >= len) OR ((c ← buffer[i]) = '.) THEN EXIT;
buffer[i] ← Ascii.Lower[c];
i ← i + 1;
ENDLOOP;
buffer.length ← i;
fixedName ← Rope.FromRefText[buffer];
};
YP Client
nullVerifier: REF TEXT ← RefText.New[0];
stamp: CARD ← BasicTime.GetClockPulses[];
stampLock: Lock ← NEW[LockObject];
GetStamp: PROC [lock: Lock ← stampLock] RETURNS [CARD] ~ {
RETURN [stamp ← stamp + 1];
};
Groups: TYPE ~ REF GroupsObject;
GroupsObject: TYPE ~ RECORD [
nGroups: CARDINAL ← 0,
groups: ARRAY [0..maxGroups) OF CARD
];
GetGroupsWithGivenMember: PROC [h: SunYPAgent.Handle, name: ROPE]
RETURNS [g: Groups] ~ {
g ← NEW[GroupsObject];
{ ENABLE SunYPAgent.Error, Convert.Error => GOTO GiveUp;
keyBefore: ROPE;
val: REF TEXT;
[keyBefore, val] ← SunYPAgent.First[h, groupMapName];
DO
seq: SunYPAgent.TextSeq;
gid: CARD;
seq ← SunYPAgent.TokenizeUsingSeparator[val, ':];
IF seq.length <= grpNumPos THEN GOTO GiveUp;
gid ← Convert.CardFromRope[RefText.TrustTextAsRope[seq.refText[grpNumPos]]];
IF seq.length <= grpMembersPos THEN GOTO GiveUp;
seq ← SunYPAgent.TokenizeUsingSeparator[seq.refText[grpMembersPos], ',];
FOR i: CARDINAL IN [0 .. seq.length) DO
IF Rope.Equal[RefText.TrustTextAsRope[seq.refText[i]], name, TRUE] THEN {
g.groups[g.nGroups] ← gid;
g.nGroups ← g.nGroups + 1;
EXIT;
};
ENDLOOP;
IF g.nGroups >= maxGroups THEN EXIT;
[keyBefore, val] ← SunYPAgent.Next[h, groupMapName, keyBefore];
ENDLOOP;
EXITS
GiveUp => NULL;
};
};
GetConversationByContactingYPServer: PROC [name, password: ROPE]
RETURNS [c: Conversation] ~ {
credentials, val: REF TEXT;
seq: SunYPAgent.TextSeq;
uid, gid: CARD;
h: SunYPAgent.Handle;
hW: SunRPC.Handle;
errorCode: ATOMNIL;
{
ENABLE {
SunYPAgent.Error => {
errorCode ← (SELECT code FROM
$noYP, $domainNotFound, $timeout => $timeout,
$keyNotFound => $wrongUserPassword,
ENDCASE => $protocol);
GOTO Cant;
};
Convert.Error => {
errorCode ← $protocol;
GOTO Cant;
};
};
h ← SunYPAgent.ObtainHandle[];
val ← SunYPAgent.Match[h, passwordMapName, name];
seq ← SunYPAgent.TokenizeUsingSeparator[val, ':];
IF seq.length <= gidPos THEN { errorCode ← $protocol; GOTO Cant };
uid ← Convert.CardFromRope[RefText.TrustTextAsRope[seq.refText[uidPos]]];
gid ← Convert.CardFromRope[RefText.TrustTextAsRope[seq.refText[gidPos]]];
IF NOT UnixPasswordMatches[uid, name, password, RefText.TrustTextAsRope[seq.refText[passwordPos]]]
THEN { errorCode ← $wrongUserPassword; GOTO Cant };
hW ← SunRPC.OpenWriter[maxCredentialsBytes];
SunRPC.PutCard32[hW, GetStamp[]];
SunRPC.PutRope[hW, ThisMachine.Name[--$Arpa--]]; -- Have to fix ThisMachineImpl!
SunRPC.PutCard32[hW, uid];
SunRPC.PutCard32[hW, gid];
{ g: Groups ← GetGroupsWithGivenMember[h, name];
SunRPC.PutCard32[hW, g.nGroups];
FOR i: CARDINAL IN [0 .. g.nGroups) DO
SunRPC.PutCard32[hW, g.groups[i]];
ENDLOOP;
};
credentials ← SunRPC.CloseWriter[hW]; hW ← NIL;
EXITS
Cant => NULL;
};
IF h # NIL THEN { SunYPAgent.ReleaseHandle[h]; h ← NIL };
IF hW # NIL THEN { [] ← SunRPC.CloseWriter[hW]; hW ← NIL };
IF errorCode # NIL THEN ERROR SunRPCAuth.Error[errorCode];
c ← NEW[ConversationObject ← [SunRPCAuth.unixFlavor, myProcs, NIL]];
c.conversationData ← NEW[ConversationDataObject ← [conversation~c, name~name, password~password, credentials~credentials]];
};
Registered Procedures
Initiate: SunRPCAuth.InitiateProc -- [flavor, myName, myPassword, hisName, registrationData] RETURNS [c] -- ~ {
IF flavor # SunRPCAuth.unixFlavor THEN ERROR;
c ← GetConversationFromCache[myName, myPassword];
IF c = NIL THEN c ← GetConversationByContactingYPServer[myName, myPassword];
};
Authenticate: SunRPCAuth.AuthenticateProc -- [cFlavor, credentials, vFlavor, verifier, registrationData] RETURNS [result, replyFlavor, replyVerifier, c] -- ~ {
This is grossly inefficient, but Cedar servers shouldn't use Unix-flavor authentication anyway, since it's so awful ...
replyFlavor ← SunRPCAuth.nullFlavor;
replyVerifier ← nullVerifier;
IF cFlavor # SunRPCAuth.unixFlavor THEN ERROR;
IF vFlavor # SunRPCAuth.nullFlavor THEN { result ← wrongVerifier; RETURN };
c ← NEW[ConversationObject ← [SunRPCAuth.unixFlavor, myProcs, NIL]];
c.conversationData ← NEW[ConversationDataObject ← [credentials~credentials]];
result ← ok;
};
ExtractConversationDetails: PUBLIC PROC [c: Conversation]
RETURNS [machineName: ROPE, uid, gid: CARD, groups: SunAuthUnix.Groups] ~ {
cD: ConversationData ← NARROW[c.conversationData];
hR: SunRPC.Handle ← SunRPC.OpenReader[cD.credentials];
{
ENABLE {
SunRPC.Error => { ERROR SunRPCAuth.Error[$badCredentials] };
UNWIND => { SunRPC.CloseReader[hR] };
};
numIDs: CARDINAL;
[] ← SunRPC.GetCard32[hR]; -- stamp
machineName ← SunRPC.GetRope[hR]; -- machineName
uid ← SunRPC.GetCard32[hR];
gid ← SunRPC.GetCard32[hR];
numIDs ← SunRPC.GetCard32[hR];
groups ← NEW[SunAuthUnix.GroupsObject[numIDs]];
FOR i: CARDINAL IN [0 .. numIDs) DO
groups.group[i] ← SunRPC.GetCard32[hR];
ENDLOOP;
};
SunRPC.CloseReader[hR];
};
Client Procs Object
GetCredentialsAndNextVerifier: SunRPCAuth.GetCredentialsAndNextVerifierProc -- [c] RETURNS [cFlavor, credentials, vFlavor, verifier] -- ~ {
cD: ConversationData ~ NARROW[c.conversationData];
IF cD.shortCredentials # NIL THEN RETURN [SunRPCAuth.shortFlavor, cD.shortCredentials, SunRPCAuth.nullFlavor, nullVerifier];
RETURN [SunRPCAuth.unixFlavor, cD.credentials, SunRPCAuth.nullFlavor, nullVerifier];
};
CheckReplyVerifier: SunRPCAuth.CheckReplyVerifierProc -- [c, flavor, verifier] RETURNS [result: SunRPCAuth.AuthenticateResult] -- ~ {
IF flavor = SunRPCAuth.nullFlavor THEN RETURN [ok];
IF flavor = SunRPCAuth.shortFlavor THEN {
cD: ConversationData ~ NARROW[c.conversationData];
cD.shortCredentials ← verifier;
RETURN [ok];
};
RETURN [wrongVerifier];
};
NoShortcut: SunRPCAuth.NoShortcutProc -- [c] RETURNS [wasShortcut] -- ~ {
cD: ConversationData ~ NARROW[c.conversationData];
wasShortcut ← (cD.shortCredentials # NIL);
cD.shortCredentials ← NIL;
};
Terminate: SunRPCAuth.TerminateProc -- [c] -- ~ {
cD: ConversationData ~ NARROW[c.conversationData];
cD.shortCredentials ← NIL;
PutConversationIntoCache[c];
};
myProcs: SunRPCAuth.Procs ← NEW[SunRPCAuth.ProcsObject ← [GetCredentialsAndNextVerifier, CheckReplyVerifier, NoShortcut, Terminate]];
Initialization
SunRPCAuth.Register[SunRPCAuth.unixFlavor, Initiate, Authenticate, Sweep, NIL];
}...