! Database manager for local access control. Each user authority record ! contains the username, (encrypted) password, login directory, local ! filesystem authorisation record (which includes user ID) and a textual ! description of the user (usually a name). There are two separate indices ! into the database, viz by username and by user ID (replicating the data ! held in the authorisation record, because of the problems of maintaining ! back-pointers into the key B-trees). Note that this process has the ! database open exclusively while it is running (i.e. all the time), so that ! any modifications to the database must be by requests to it. This process ! does not (at the moment) manage any of the other files which might be ! involved in user accreditation and management. %externalstring(47) copyright %alias "GDMR_(C)_LOCALACC" = %c "Copyright (C) 1987 George D.M. Ross" %option "-nonstandard-nocheck-nodiag-noline-nostack" !%option "-nonstandard" %include "Moose:Mouse.Inc" %include "GDMR_H:FSysAcc.Inc" %include "GDMR_H:FSys.Inc" %include "GDMR_H:Tree.Inc" %include "GDMR_H:Dir.Inc" %externalroutinespec fsys initialise %externalintegerfnspec fsys get full ID(%integer partial, %integername full) %systemroutinespec phex(%integer i) %externalintegerfnspec free store %externalpredicatespec FS lookup(%string(31) key, %integername value) %externalroutinespec FS insert(%string(31) key, %integer value) %constinteger database path last = 3 %ownstring(15)%array database path(1 : database path last) = "System", "Authority", "Local_Database" %conststring(31) database listing path = "System:Authority:Local_Listing" %ownrecord(semaphore fm) request semaphore = 0 %ownrecord(mailbox fm) request mailbox = 0 %conststring(31) local authority mailbox = "LOCAL_AUTHORITY_MAILBOX" %recordformat authority message fm(%record(message fm) system part, %integer code, result, %string(*)%name username, %string(*)%name password, old password, %record(fsys access fm)%name fsys access, %string(*)%name default directory, %string(*)%name textual description) %constinteger enquire user = 1 %constinteger validate user = 2 %constinteger create user = 3 %constinteger modify password = 4 %constinteger modify user = 5 %constinteger delete user = 6 %constinteger print by username = 7; ! We do the printing!! %constinteger print by ID = 8; ! We do the printing!! ! Diagnostic %routine zprintstring(%string(255) s) %integer i, ch %return %if s = "" %for i = 1, 1, length(s) %cycle ch = charno(s, i) %if ' ' <= ch <= '~' %start print symbol(ch) %else print symbol('<') write(ch, 0) print symbol('>') %finish %repeat %end ! Database manipulation stuff... %constinteger root directory ID = 16_01000002 %owninteger database ID = 0 %owninteger database token = 0 %constinteger key not found error = -501 ! Password encryption, *based* on ECMA CRC as used in old-style filestores ! (supplied by Gordon Brebner). %integerfn encrypt(%string(255) user, password) %integer rem, p %routine include(%byte b) %integer i, flag %for i = 0, 1, 7 %cycle flag = rem rem = rem<<1 ! b&1 rem = rem !! 2_00000100110000010001110110110111 %if flag < 0 b = b>>1 %repeat %end !! printstring("Encrypt: """); zprintstring(user) !! printstring(""" """); zprintstring(password) !! print symbol('"'); newline rem = 16_FFFFFFFF to upper(user); to upper(password) include(charno(user, p)) %for p = 1, 1, length(user) %if password # "" %start include(charno(password, p)) %for p = 1, 1, length(password) %finish !! printstring("Encrypted to "); phex(rem); newline %result = rem %end ! User record manipulation routines... ! User record fields appear in the database in the following order: ! Pointers to authorisation record, login directory, description ! Password (first, because it's fixed in size (4 bytes)) ! Username ! Authorisation record ! Login directory ! Textual description %recordformat database fm(%byte authorisation offset, login offset, %byte description offset, spare, %integer encrypted password, %shortarray x(1 : 250)) %recordformat user fm(%integer size, %record(database fm) U) {} %routine display user record(%record(user fm)%name U) {} %record(fsys access fm)%name a {} printstring("User record at "); phex(addr(U)) {} printstring(", size "); write(U_size, 0); newline {} printstring("User: "); zprintstring(string(addr(U_U_x(1)))) {} a == record(addr(U_U_x(U_U_authorisation offset))) {} printstring(", ID "); write(a_user ID, 0); newline {} printstring("Login: ") {} zprintstring(string(addr(U_U_x(U_U_login offset)))) {} printstring(", description: ") {} zprintstring(string(addr(U_U_x(U_U_description offset)))); newline {} %end %integerfn get user by name(%string(*)%name user, %integername site, %record(user fm)%name U) %integer status !! printstring("Get user by name: "); printstring(user); newline status = B tree find entry(database token, user, site) %result = status %if status # 0 %or U == nil !! printstring("Found at "); phex(site) !! printstring(", now get data"); newline %result = B tree data value(database token, site, U_size, U_U) %end %integerfn get user by ID(%integer user ID, %integername site, %record(user fm)%name U) %string(5) ID key %integer status !! printstring("Get user by ID: "); phex(user ID); newline shortinteger(addr(ID key)) = 16_0500 integer(addr(ID key) + 2) = user ID status = B tree find entry(database token, ID key, site) %result = status %if status # 0 %or U == nil %result = B tree data value(database token, site, U_size, U_U) %end %integerfn update user(%record(user fm)%name U, %integer site) ! site < 0: new site required & create new keys ! = 0: new site required & update keys ! > 0: in-situ update (no key modification required) %record(fsys access fm)%name a %string(5) ID key %string(*)%name user key %integer status, new site %if site > 0 %start ! In-situ modification (assume no size change) %result = B tree data replace(database token, site, U_U) %finish ! Otherwise we'll need to update the keys too a == record(addr(U_U_x(U_U_authorisation offset))) shortinteger(addr(ID key)) = 16_0500 integer(addr(ID key) + 2) = a_user ID user key == string(addr(U_U_x(1))) status = B tree data insert(database token, U_size, U_U, new site) %result = status %if status # 0 %if site = 0 %start ! Replace existing record status = B tree modify entry(database token, user key, new site) %result = status %if status # 0 %result = B tree modify entry(database token, ID key, new site) %else ! Inserting from scratch, so create keys too... status = B tree add entry(database token, user key, new site) %result = status %if status # 0 %result = B tree add entry(database token, ID key, new site) %finish %end %routine new user record(%record(user fm)%name U, %string(255) username, login directory, %record(fsys access fm)%name a, %string(255) textual description) U = 0 string(addr(U_U_x(1))) = username U_size = length(username) // 2 + 1 U_U_authorisation offset = u_size + 1 record(addr(U_U_x(U_U_authorisation offset))) = a U_size = U_size + fsys access size + 4 * a_groups U_U_login offset = U_size + 1 string(addr(U_U_x(U_U_login offset))) = login directory U_size = U_size + length(login directory) // 2 + 1 U_U_description offset = U_size + 1 string(addr(U_U_x(U_U_description offset))) = textual description U_size = U_size + length(textual description) // 2 + 1 ! Now convert size to bytes, including password & pointers U_size = 2 * U_size + 8 %end ! Initial database opening routine. If the database doesn't exist, we create ! it (the final directory must, however, exist). ! The database is normally open, and is only closed to commit/abandon a ! transaction (insertion, removal or modification of an entry). %routine lookup database %integer directory ID, status, i, j %string(255) text fsys initialise status = fsys get full ID(root directory ID, database ID) %if status # 0 %start printstring("Failed to find root directory's ID: ") write(status, 0); newline %stop %finish database ID = database ID ! directory flag %for i = 1, 1, database path last %cycle directory ID = database ID status = directory lookup one(nil, 0, directory ID, database path(i), 0, database ID, text) %if status # 0 %start %if i = database path last %and status = key not found error %start ! No database, so we'll create a new one printstring("Creating new authority database ") %for j = 1, 1, database path last %cycle printstring(database path(j)) print symbol(':') %if j # database path last %repeat newline status = B tree create(nil, 0, database path(database path last), directory ID >> partition shift, directory ID, database ID) %if status # 0 %start printstring("Failed to create new authority database: ") write(status, 0); newline %stop %finish status = directory insert ID(nil, 0, directory ID, database path(database path last), database ID) %if status # 0 %start printstring("Failed to insert authority database's entry: ") write(status, 0); newline %finish %exit %else printstring("Lookup status "); write(status, 0) printstring(" for "); printstring(database path(i)); newline %stop %finish %finish %repeat %end %routine close database(%integer abandon) %integer status status = B tree close(database token, abandon) %if status # 0 %start printstring("Close authority database: "); write(abandon, 0) space; write(status, 0); newline %finish %end %routine open database %record(user fm) U %record(fsys access fm) a %integer status, i lookup database %if database ID = 0 status = B tree open by ID(nil, 0, database ID, read access ! modify access, database token, i) %if status # 0 %start printstring("Failed to open authority database: ") write(status, 0); newline %stop %finish %end ! Request action routines proper %routine do enquire user(%record(authority message fm)%name m) %record(user fm) U %integer status, x !! printstring("Do enquire user "); printstring(m_username) !! newline status = get user by name(m_username, x, U) m_result = status %and %return %if status # 0 !! printstring("User "); printstring(m_username) !! printstring(", site "); phex(x); newline !! display user record(U) m_fsys access = record(addr(U_U_x(U_U_authorisation offset))) m_default directory = string(addr(U_U_x(U_U_login offset))) m_textual description = string(addr(U_U_x(U_U_description offset))) m_result = 0 %end %routine do validate user(%record(authority message fm)%name m) %record(user fm) U %integer status, x, size !! printstring("Do validate user: "); printstring(m_username) !! printstring(", pass: "); printstring(m_password); newline status = get user by name(m_username, x, U) m_result = status %and %return %if status # 0 !! display user record(U) m_result = -1 %and %return %c %unless U_U_encrypted password = encrypt(m_username, m_password) m_fsys access = record(addr(U_U_x(U_U_authorisation offset))) m_default directory = string(addr(U_U_x(U_U_login offset))) m_result = 0 %end %routine do create user(%record(authority message fm)%name m) %record(user fm) U !! printstring("Do create user: "); printstring(m_username) !! newline new user record(U, m_username, m_default directory, m_fsys access, m_textual description) U_U_encrypted password = encrypt(m_username, m_password) m_result = update user(U, -1) close database(m_result) open database %end %routine do delete user(%record(authority message fm)%name m) %record(user fm) U %record(fsys access fm)%name a %string(5) ID key %integer status, site, size !! printstring("Do delete user: "); printstring(m_username) !! newline status = get user by name(m_username, site, U) m_result = status %and %return %if status # 0 !! display user record(U) a == record(addr(U_U_x(U_U_authorisation offset))) m_default directory = string(addr(U_U_x(U_U_login offset))) shortinteger(addr(ID key)) = 16_0500 integer(addr(ID key) + 2) = a_user ID !! printstring("Deleting data record at "); phex(site); newline m_result = B tree data delete(database token, site) %if m_result = 0 %start !! printstring("Deleting key "); printstring(m_username); newline m_result = B tree delete entry(database token, m_username) %if m_result = 0 %start !! printstring("Deleting key "); zprintstring(ID key); newline m_result = B tree delete entry(database token, ID key) %finish %finish !! printstring("Delete: result "); write(m_result, 0); newline close database(m_result) open database %end %routine do modify password(%record(authority message fm)%name m) %record(user fm) U %integer site, status !! printstring("Do modify password: "); printstring(m_username) !! newline status = get user by name(m_username, site, U) m_result = status %and %return %if status # 0 U_U_encrypted password = encrypt(m_username, m_password) m_result = update user(U, site) close database(m_result) open database %end %routine do modify user(%record(authority message fm)%name m) %record(user fm) U, new U %record(fsys access fm)%name a %integer status, site, size !! printstring("Do modify user: "); printstring(m_username) !! newline status = get user by name(m_username, site, U) m_result = status %and %return %if status # 0 !! display user record(U) a == record(addr(U_U_x(U_U_authorisation offset))) m_result = -1 %and %return %if a_user ID # m_fsys access_user ID new user record(new U, m_username, m_default directory, m_fsys access, m_textual description) new U_U_encrypted password = U_U_encrypted password !! display user record(new U) %if new U_size = U_size %start ! Can update in situ m_result = update user(new U, site) %else ! Change in size, must delete old and create new m_result = B tree data delete(database token, site) m_result = update user(new U, 0) %if m_result = 0 %finish close database(m_result) open database %end %routine do print database(%record(authority message fm)%name m) %record(user fm) U %record(fsys access fm)%name access %record(key list fm)%name k, next %string(*)%name s %integer status, i %on 3 %start select output(3) printstring("Failed to open database listing file ") printstring(database listing path); newline %return %finish k == B tree key list(database token, status) %if status # 0 %start printstring("Failed to obtain database key list: "); write(status, 0) newline %return %finish open output(1, "x$Passwords") open output(2, "x$UserData") open output(3, "x$UserIDs") %while k ## nil %cycle %if charno(k_key, 1) # 0% %start status = B tree data value(database token, k_value, U_size, U_U) %if status = 0 %start s == string(addr(U_U_x(1))) select output(1); ! Passwords printstring(s); spaces(15 - length(s)) printstring("16_"); phex(U_U_encrypted password & 16_7FFFFFFF) newline select output(2); ! UserData printstring(s); spaces(15 - length(s)) s == string(addr(U_U_x(U_U_login offset))) printstring(s); spaces(15 - length(s)) s == string(addr(U_U_x(U_U_description offset))) print symbol('"'); printstring(s); print symbol('"') newline select output(3); ! UserIDs s == string(addr(U_U_x(1))) printstring(s); spaces(15 - length(s)) access == record(addr(U_U_x(U_U_authorisation offset))) write(access_user ID, 6); space write(access_privileges, 4) write(access_supervisor ID, 4) space %and write(access_group(i), 1) %for i = 1, 1, access_groups newline %else select output(3) zprintstring(k_key) printstring(": Failed to obtain data record from ") phex(k_value); printstring(", reason ") write(status, 0); newline %finish %finish next == k_next; dispose(k) k == next %repeat select output(1); close output select output(2); close output select output(3); close output select output(3) %end %conststring(31) local authority name = "FS_LOCAL_AUTHORITY_RECORD" %ownrecord(fsys access fm) local authority record = 0 %begin %record(authority message fm)%name request %ownrecord(semaphore fm) disaster = 0 %record(poa fm)%name P %integer i %on 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 %start ! Last-chance disaster-trap P == POA printstring("LocalAcc: unexpected event "); write(P_event, 0) space; write(P_event sub, 0); space; phex(P_event extra) space; printstring(P_event message) printstring(" at or about PC "); phex(P_event PC) newline %for i = 0, 1, 15 %cycle phex(P_event r(i)); space newline %if i & 7 = 7 %repeat semaphore wait(disaster) %finish open input(3, ":N"); select input(3) open output(3, ":T"); select output(3) setup semaphore(disaster) mark %if POA_heap_level = 1 printstring("Authority server starting: "); write(free store, 0) printstring(" bytes free"); newline setup semaphore(request semaphore) setup mailbox(request mailbox, request semaphore) FS insert(local authority mailbox, addr(request mailbox)) local authority record = 0 local authority record_user ID = 1 FS insert(local authority name, addr(local authority record)) open database %cycle request == receive message(request mailbox) %if request_code = enquire user %start do enquire user(request) %else %if request_code = validate user do validate user(request) %else %if request_code = create user do create user(request) %else %if request_code = modify password do modify password(request) %else %if request_code = modify user do modify user(request) %else %if request_code = delete user do delete user(request) %else %if request_code = print by username %c %or request_code = print by ID do print database(request) request_result = 0 %else request_result = -2 %finish send message(request, request_system part_reply, nil) %repeat %end %of %program