// IFSAccessControl.bcpl // Copyright Xerox Corporation 1981, 1982 // Last modified April 11, 1982 11:18 AM by Taft get "Ifs.decl" get "IfsFiles.decl" get "IfsDirs.decl" get "IfsRs.decl" get "Grapevine.decl" get "GrapevineProtocol.decl" external [ // outgoing procedures CheckAccess; CheckAllocation UserOwns; RNamesEqual; MakeQualifiedRName; StripDefaultReg // incoming procedures GetDIF; UpdateCachedDIF; GetGroupName; IsInACL ExtractSubstring; StringCompare; GetBit; SetBit; ExpandTemplate DefaultArgs; SysFree; FreePointer; MoveBlock; Zero; DoubleUsc // outgoing statics enableGrapevineAuth; enableGrapevineGroup; defaultRegistry // incoming statics CtxRunning ] static [ enableGrapevineAuth = false // authenticate via Grapevine if true enableGrapevineGroup = false // group membership via Grapevine if true defaultRegistry = 0 // -> default Grapevine registry name if nonzero ] // special group number for obtaining the real name of the "World" group manifest groupNumWorld = size Protection //---------------------------------------------------------------------------- let UserOwns(fd, userInfo; numargs na) = valof //---------------------------------------------------------------------------- // Returns true if the user described by userInfo is the owner of // the file described by fd. The file need not exist. [ DefaultArgs(lv na, -1, CtxRunning>>RSCtx.userInfo) let dirName = ExtractSubstring(lv fd>>FD.dr>>DR.pathName, 2, fd>>FD.lenDirString-1) // compare connected & login directory names to "<dir>" portion of filename let res = RNamesEqual(userInfo>>UserInfo.connName, dirName)? true, RNamesEqual(userInfo>>UserInfo.userName, dirName)? true, false SysFree(dirName) resultis res ] //---------------------------------------------------------------------------- and RNamesEqual(name1, name2) = valof //---------------------------------------------------------------------------- // Returns true iff name1 is equal to name2, treated as R-Names. // If either name is unqualified it is assumed to belong to defaultRegistry. [ if name1>>String.length gr name2>>String.length then [ let t = name1; name1 = name2; name2 = t ] // exchange names // now name1.length le name2.length switchon StringCompare(name1, name2) into [ case 0: // exact match resultis true case -2: // name1 is an initial substring of name2 if defaultRegistry ne 0 then [ // see if tail is ".<defaultRegistry>" let iDot = name1>>String.length+1 if name2>>String.char↑iDot eq $. & StringCompare(name2, defaultRegistry, iDot+1) eq 0 resultis true ] ] resultis false ] //--------------------------------------------------------------------------- and MakeQualifiedRName(name) = valof //--------------------------------------------------------------------------- // Returns a new string which is the fully-qualified form of name, // using defaultRegistry if name is not already qualified. [ if defaultRegistry eq 0 resultis ExtractSubstring(name) for i = 1 to name>>String.length do if name>>String.char↑i eq $. resultis ExtractSubstring(name) resultis ExpandTemplate("$S.$S", name, defaultRegistry) ] //--------------------------------------------------------------------------- and StripDefaultReg(name) = valof //--------------------------------------------------------------------------- // Strips the registry from name if it is the default registry. // Returns true if it stripped the registry and false if not. [ if defaultRegistry ne 0 then for i = name>>String.length to 1 by -1 do if name>>String.char↑i eq $. & StringCompare(name, defaultRegistry, i+1) eq 0 then [ name>>String.length = i-1; resultis true ] resultis false ] //---------------------------------------------------------------------------- and CheckAllocation(difRec, userInfo; numargs na) = valof //---------------------------------------------------------------------------- [ DefaultArgs(lv na, -1, CtxRunning>>RSCtx.userInfo) resultis userInfo>>UserInfo.capabilities.wheel ne 0 % DoubleUsc(lv difRec>>DIFRec.diskPageUsage, lv difRec>>DIFRec.diskPageLimit) ls 0 ] //---------------------------------------------------------------------------- and CheckAccess(prot, owner, userInfo; numargs na) = valof //---------------------------------------------------------------------------- // Checks whether the user described by userInfo has access to // the entity protected by the Protection "prot". "owner" should // be true if the user owns the entity in question and false // otherwise. Returns true if access is allowed. [ DefaultArgs(lv na, -2, CtxRunning>>RSCtx.userInfo) if userInfo>>UserInfo.capabilities.wheel resultis true let userGroups = lv userInfo>>UserInfo.userGroups userGroups>>Protection.owner = owner unless enableGrapevineGroup do userGroups>>Protection.world = true // First check the cached group membership for i = 0 to lenProtection-1 do if (userGroups!i & prot!i) ne 0 resultis true unless enableGrapevineGroup resultis false // Local cache failed; do Grapevine access check. // For each group present in the protection but absent in the user's // locally-known group membership, ask Grapevine whether the user is a // member of the group, and update the locally-known group membership. // To minimize time wasted asking Grapevine about groups that the user is // not a member of (which is relatively expensive), we check groups in // two passes. During the first pass skip over the groups indicated in // the user's hintNotUserGroups. Only if this fails do we check those // groups (in case the hint has become obsolete). // CheckAccess (cont'd) let hintNotUserGroups = vec lenProtection let dif = GetDIF(userInfo>>UserInfo.userName) test dif eq 0 ifso Zero(hintNotUserGroups, lenProtection) ifnot [ unless dif>>DIF.validGrapevineRName do [ SysFree(dif); resultis false ] // not in Grapevine, no point asking MoveBlock(hintNotUserGroups, lv dif>>DIF.hintNotUserGroups, lenProtection) ] let difChanged = false let ignoreHint = false let result = valof [ // repeat // do groups in reverse numerical order so as to pick up World first for group = size Protection-1 to 0 by -1 do if group ne offset Protection.owner & GetBit(prot, group) & not GetBit(userGroups, group) & GetBit(hintNotUserGroups, group) eq ignoreHint then [ let groupName = GetGroupName((group eq offset Protection.world? groupNumWorld, group)) let ec = ecBadRName if groupName ne 0 then [ // ask Grapevine: IsMember, UpArrow closure ec = IsInACL(groupName, userInfo>>UserInfo.userName, dItself+dMember+dUpArrow) SysFree(groupName) ] switchon ec into [ case ecIsMember: SetBit(userGroups, group, true) if dif ne 0 then [ SetBit(lv dif>>DIF.userGroups, group, true) SetBit(lv dif>>DIF.hintNotUserGroups, group, false) difChanged = true ] resultis true case ecBadRName: // If "World" group is not specified or is unknown to // Grapevine then let it include everyone. For other groups, // if no group name is specified or Grapevine never heard // of the name, consider the user not to be a member. if group eq offset Protection.world then docase ecIsMember case ecIsNotMember: case ecAllDown: // not a member if Grapevine is unresponsive if dif ne 0 & not ignoreHint then [ SetBit(lv dif>>DIF.hintNotUserGroups, group, true) difChanged = true ] endcase ] ] if ignoreHint then resultis false ignoreHint = true ] repeat if difChanged then UpdateCachedDIF(userInfo>>UserInfo.userName, dif) FreePointer(lv dif) resultis result ]