// IfsLogConn.bcpl - authentication (Login and Connect) // Copyright Xerox Corporation 1979, 1981, 1982 // Last modified April 11, 1982 3:01 PM by Taft get "Ifs.decl" get "IfsFiles.decl" get "IfsDirs.decl" get "Grapevine.decl" external [ // outgoing procedures CreateUserInfo; DestroyUserInfo; Login; Connect; FindDIF // incoming procedures GetDIF; UpdateCachedDIF; CheckAccess; MakeQualifiedRName; WheelCall IFSOpenFile; FilePos; ChangeFileAttributes; StreamsFD; PutTemplate; Closes MakeKey; Authenticate StringCompare; ExtractSubstring; RNamesEqual Password; ReadCalendar; DoubleSubtract; DoubleUsc SysAllocateZero; SysFree; Zero; MoveBlock; FreePointer; DefaultArgs // outgoing statics enablePasswordLog // incoming statics enableGrapevineAuth; enableGrapevineGroup ] static [ enablePasswordLog = false ] // User name conventions: // If Grapevine authentication is not enabled, user and connect names // must exactly match the names of the DIFs to which access is being gained. // If Grapevine authentication is enabled, user names not qualified by // registry are assumed to belong to the default registry, except in the // case where the qualified name does not exist in Grapevine but does exist // (qualified or unqualified) as a local DIF. // UserInfo.userName is always fully-qualified except in the latter case. // With respect to presence or absence of qualification, UserInfo.connName // always matches the name of the local DIF, and it is not possible to // connect to a directory for which no DIF exists. //--------------------------------------------------------------------------- let CreateUserInfo() = SysAllocateZero(lenUserInfo) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and DestroyUserInfo(ui) be //--------------------------------------------------------------------------- [ FreePointer(lv ui>>UserInfo.userName, lv ui>>UserInfo.connName, lv ui>>UserInfo.defaultDir, lv ui) ] //--------------------------------------------------------------------------- and Login(name, password, ui, force; numargs na) = valof //--------------------------------------------------------------------------- // Name may be qualified or unqualified; if unqualified, defaultRegistry // is assumed. If force then suppress files-only and password check. // Returns: 0 if ok, an error code otherwise. // Updates UserInfo structure iff successful (changes only userName, // connName, capabilities, and userGroups). [ if na le 3 then force = false if name eq 0 % name>>String.length eq 0 resultis ecNamePasswordRequired let res = 0 if ui>>UserInfo.userName eq 0 % // currently not logged in? not RNamesEqual(name, ui>>UserInfo.userName) then // different name? [ let difName = 0 let dif = GetDIF(name, false, lv difName) res = valof [ if dif eq 0 then [ // no local DIF found. We can now succeed only if Grapevine // authenticates the name/password; and login cannot be forced. if not enableGrapevineAuth % force resultis ecUserName // set up a blank DIF that perhaps will be validated by Grapevine. dif = SysAllocateZero(lenDIF) dif>>DIF.nonexistentDIF = true difName = MakeQualifiedRName(name) ] unless force do [ if dif>>DIF.filesOnly resultis ecFilesOnly let ec = ValidateDIF(dif, difName, password) if ec ne 0 resultis ec ] // successful login. Update names in UserInfo block. // userName is fully qualified except in the case of a name for // which a local DIF exists but which Grapevine never heard of. // connName always matches the local DIF (if there is one). FreePointer(lv ui>>UserInfo.userName, lv ui>>UserInfo.connName, lv ui>>UserInfo.defaultDir) ui>>UserInfo.userName = dif>>DIF.validGrapevineRName? MakeQualifiedRName(difName), ExtractSubstring(difName) ui>>UserInfo.connName = difName difName = 0 if enablePasswordLog & not force then [ // Password logging hack let s = WheelCall(IFSOpenFile, "<System>Ifs.test", 0, modeAppend) if s ne 0 then [ if FilePos(s) eq 0 then WheelCall(ChangeFileAttributes, StreamsFD(s), ZeroAttr) PutTemplate(s, "$S $S*n", ui>>UserInfo.userName, password) Closes(s) ] ] ui>>UserInfo.capabilities = dif>>DIF.capabilities MoveBlock(lv ui>>UserInfo.userGroups, lv dif>>DIF.userGroups, lenProtection) resultis 0 ] FreePointer(lv difName, lv dif) ] resultis res ] //--------------------------------------------------------------------------- and Connect(name, password, ui) = valof //--------------------------------------------------------------------------- // name = 0 or name.length = 0 means connect to the user's login directory. // Returns: 0 if ok, an error code otherwise. // Updates connName in UserInfo structure iff successful. // Checks password only if something has changed. [ if name eq 0 % name>>String.length eq 0 then name = ui>>UserInfo.userName let res = 0 unless RNamesEqual(name, ui>>UserInfo.connName) do [ let difName = 0 let dif = GetDIF(name, false, lv difName) res = valof [ if dif eq 0 % dif>>DIF.nonexistentDIF then // permit connecting to nonexistent DIF only if Grapevine // authentication is enabled and the connect name matches // the login name. unless RNamesEqual(difName, ui>>UserInfo.userName) do resultis ecConnectName // Permit connect to succeed if: // (1) connecting to login directory; // (2) user is owner of directory; // (3) user is member of connect groups of directory; or // (4) password is the correct one for the directory. unless RNamesEqual(difName, ui>>UserInfo.userName) % // (1) RNamesEqual(ui>>UserInfo.userName, lv dif>>DIF.owner) % // (2) CheckAccess(lv dif>>DIF.connectProt, false) % // (3) ValidateDIF(dif, difName, password) eq 0 do // (4) resultis ecConnectPassword // success. Install actual DIF name (if it exists) as connect name. SysFree(ui>>UserInfo.connName) ui>>UserInfo.connName = difName difName = 0 resultis 0 ] FreePointer(lv dif, lv difName) ] resultis res ] //--------------------------------------------------------------------------- and ZeroAttr(fd, ld, nil) = valof //--------------------------------------------------------------------------- [ Zero(lv ld>>ILD.fileProt, lenFileProt) ld>>ILD.noBackup = true resultis true ] //--------------------------------------------------------------------------- and ValidateDIF(dif, name, password) = valof //--------------------------------------------------------------------------- // Interacts with Grapevine, if appropriate, to verify that name and // password are (still) authentic. If necessary, updates the DIF. // To call ValidateDIF for a nonexistent local directory, the caller must // construct a blank DIF -- a block of length lenDIF whose contents are all // zero. A nonexistent DIF is recognizable by its nonexistentDIF bit // being true. In this case, if validation is successful, the password is // filled in and the DIF is inserted into the cache. // Returns zero if successful, or an error code describing the outcome: // ecUserName DIF is nonexistent and Grapevine never heard of name. // ecUnknownUserName DIF is nonexistent and Grapevine is unresponsive. // ecUserPassword Either Grapevine rejected the password, or Grapevine // is unresponsive and the password does not match DIF. [ if password eq 0 % password>>String.length eq 0 resultis ecUserPassword let difPassword = lv dif>>DIF.password let localPwdOk = difPassword!0 ne 0 & Password(password, difPassword, false) if enableGrapevineAuth then [ let time = vec 1; ReadCalendar(time) DoubleSubtract(time, table [ 0; 124300B ]) // 12 hours unless localPwdOk & DoubleUsc(lv dif>>DIF.timeLastValid, time) gr 0 do [ // password wrong or DIF is stale -- time to ask Grapevine again let key = vec lenPassword MakeKey(password, key) let rName = MakeQualifiedRName(name) let ec = Authenticate(rName, key) SysFree(rName) switchon ec into [ case ecIndividual: // successfully authenticated -- update the DIF Password(password, difPassword, true) ReadCalendar(lv dif>>DIF.timeLastValid) dif>>DIF.validGrapevineRName = true if enableGrapevineGroup then // forget user group membership -- require it to be // determined afresh from Grapevine when needed. Zero(lv dif>>DIF.userGroups, lenProtection) UpdateCachedDIF(name, dif) resultis 0 case ecBadPassword: resultis ecUserPassword case ecBadRName: if localPwdOk then [ // Grapevine doesn't know about this name, but we do and // the password matches the local DIF. ReadCalendar(lv dif>>DIF.timeLastValid) dif>>DIF.validGrapevineRName = false UpdateCachedDIF(name, dif) ] endcase // case ecAllDown: default: // depend on the information in the local DIF, but do not // update its timeLastValid. if difPassword!0 eq 0 resultis ecUnknownUserName ] ] ] resultis localPwdOk? 0, difPassword!0 eq 0? ecUserName, ecUserPassword ]