   dim Version$/"LOGOFF V1.0a (C) 1985 Software Dynamics"/
!  LOGOFF is a program run by SDOS/MT at boot time, or by a user
!  when he is through using an SDOS system.  Its primary purpose
!  is to prevent unauthorized users from gaining access to an SDOS system.
!  This is useful for systems used in high-traffic areas, or
!  on systems which have dial-in ports.
!
!  The program has two modes of use: NORMAL and SUPPORT.
!  Normal mode is the default, and occurs when the program is started
!  with no parameters.  In Normal mode, the program merely waits
!  for a keyboard entry matching a person's ID, and then asks
!  for a password.  If the ID and the password match a User profile
!  data base, then the user is allowed access to the computer.
!  (If one desires anybody to use the system, one simply adds a user
!  with the ID "ANONYMOUS" and an empty password).
!  A failed match causes denial of access to the computer.
!  Failed attempts get recorded in a log file so
!  that attempts to crack a system can be traced.
!  A successful attempt is called a LOGIN, and the ID of the logged in person
!  is timestamped and recorded in the LOGOFF.DATA file.
!  The file LOGOFF.NEWS is printed so that system messages can
!  be seen by all users.
!  Initial access to the computer causes a particular program, dependent
!  on the user ID, to fetched; normally, this is DEFAULTPROGRAM,
!  but it may be something else if one wishes to restrict the user
!  to running a particular program.  Normal mode runs KILLPROOF, so
!  it cannot be aborted by a conventional user.  KILLPROOF mode is
!  removed if the user profile so states.
!
!  Support mode is entered when the LOGOFF program is started with
!  a non-empty parameter line matching the current support password.
!  In support mode, one can add new ID and profile information,
!  delete a user ID and profile information, change a
!  user's password or profile (by deleting and re-inserting the ID);
!  or one can adjust the LOGOFF.DATA file.  These functions cannot
!  be performed by users not in support mode, as the LOGOFF.DATA file
!  has its contents stored in an encrypted format.
!
!  LOGOFF.DATA file format:
!
!        16 Bytes    LOGOFF data file interlock object ID.
!        64 Bytes    System ID message.  Printed at each login attempt.
!        16 Bytes    LOGOFF Support Password
!        6 Bytes     Number of user records
!        57 Bytes    User Record.  Format as follows:
!                    16 Bytes:  User ID, blank padded
!                    16 Bytes:  User password, blank padded
!                    1 Byte:  KILLENABLE flag
!                    24 Bytes: Name of program to run on LOGIN
!
!        Ascii line: User LOGIN record
!                    8 bytes: "LOGIN" "LOGOUT" or "FAILED"
!                    ~15 Bytes: MM/DD/YY HH:MM (time of login attempt)
!                    16 Bytes: User ID, blank padded
!                    16 Bytes: User password if login attempt failed
!
Dim LogoffInterlock$(16),SystemID$(64),SupportPassword$(16),NumberOfUserIDs

Dim UserID$(16),UserPassword$(16),KillEnableFlag$(1),UserInitialProgram$(24)
Dim UserRecordSize/57/,UserRecordBase/102/

Dim EnteredID$(16),EnteredPassword$(16),...
&   EnteredKillEnableFlag$(1),EnteredUserInitialProgram$(24)

Dim UserRecord$(57),OldUserRecord$(57)

Dim LogoffDataFile$/"LOGOFF.DATA"/
!
Dim Line$(128)
!
Dim CCPositionToEnd$/:E,4,0,:13/
Dim CCSetFileSize$/:E,4,0,:12/
Dim CCSetFileProt$/:E,8,0,:11/,WriteProtect$/:40/,NoProtect$/:0/

Dim SyscallKillProof$/:16,2/,SyscallKillEnable$/:17,2/

Dim SyscallDelayThirtySeconds$/:1C,4,:07,08/

Dim CCEchoEnable$/:E,4,0,:10/,CCNoEcho$/:E,4,0,:11/

Dim ILCreateInterlock$/:1B,14,0,0/
Dim ILLockInterlock$/:1B,14,0,3/,ILReleaseInterlock$/:1B,14,0,4/

Subroutine LockDataBase
   ! Subroutine to set things up so it is safe to read Database
   ! Also get Database parameters
   Open #1,LogoffDataFile$ \ ! Assert: this file exists
!!!!! I think the error recovery for non-existent db s/b in Lock/Setup!
   Read #1,LogoffInterlock$
   Syscall ILLockInterlock$,LogoffInterlock$ \ ! Wait for safe access
   Read #1,SystemID$,SupportPassword$,NumberOfUserIDs
   Return Subroutine
End

Subroutine SetupToModifyDataBase
   ! Subroutine to set things up so it is safe to modify Database
   ! Also get Database parameters
   Open #1,LogoffDataFile$ \ ! Assert: this file exists
   Read #1,LogoffInterlock$
   Syscall ILLockInterlock$,LogoffInterlock$ \ ! Wait for safe access
   Read #1,SystemID$,SupportPassword$,NumberOfUserIDs
   Syscall #1,CCSetFileProt$,NoProtect$
   Return Subroutine
End

Subroutine ReleaseDataBase
   ! Subroutine to clean up after database access
   Syscall #1,CCSetFileProt$,WriteProtect$
   Syscall #1,ILReleaseInterlock$,LogoffInterlock$
   Close #1
   Return Subroutine
End
!^L
! **** Main program starts here ***
LOGOFF: ! Control transfers here when LOGOFF is started.
   If Col(0)>1
   Then
       ! Command line parameter was given, fetch it.
       Input "" Line$
       Print Version$
       ! Check to see parameter given matches Support password.
       If Error When
          Open #1,LogoffDataFile$
       Then
           If Err=1011
           Then Print "??? ";LogoffDataFile$;" file doesn't exist."\Error
           Else Error Fi
       Fi
       Close #1 \ ! So LockDataFile below doesn't blow up
       LockDataBase \ ! and fetch SupportPassword$
       ReleaseDataBase
       len(EnteredPassword$)=maxlen(EnteredPassword$)
       EnteredPassword$[1,len(EnteredPassword$)]=lowercase$(line$) \ ! make lower case and pad to 16 chars
       If EnteredPassword$=SupportPassword$ then PrintSupportModeMenu Fi
       ! Password doesn't match. Simply logout.
   Else
       Line$="" \ ! So something goes into logout record
   Fi
DoLogout: ! Log this user off the system.
   Print "User logged off at ";time$;" ";date$
   If Error When Open #1,LogoffDataFile$
   Then
       If Err=1011
       Then
           Print LogoffDataFile$;" file doesn't exist."
           Input "Shall I create it? " Line$
           Let Line$=LowerCase$(Line$)
           If Line$<>"yes, because i say so!"
           Then
               Print "*** Can't log you out: report to system manager. ***"
               Exit
           Fi
           Create #1,LogoffDataFile$
           len(line$)=16 \ ! Make dummy Interlock$ capability
           Syscall ILCreateInterlock$,Line$,LogoffInterlock$
           Syscall ILLockInterlock$,LogoffInterlock$
           len(SystemID$)=maxlen(SystemID$)
           Input "System ID: " SystemID$[1,len(SystemID$)] \ ! Blank fill
           len(SupportPassword$)=maxlen(SupportPassword$)
           Input "Support Password: " SupportPassword$[1,len(SupportPassword$)]
           let SupportPassword$=lowercase$(SupportPassword$)
           NumberOfUserIDs=0 \ ! How many IDs we have collected
           ! Store initializing data into data base.
           Write #1,LogoffInterlock$,SystemID$,SupportPassword$,NumberOfUserIDs
           ReleaseDataBase
           Print LogoffDataFile$;" created."
           Print "BE SURE TO PUT IN YOUR OWN USERID AND PASSWORD!"
           Goto PrintSupportModeMenu
       Fi
   Fi
   Close #1
   SetupToModifyDataBase \ ! and fetch SystemID$ for login time banner
   Syscall #1,CCPositionToEnd$
   Print #1,"LOGOFF ";date$;" ";time$;" ";line$
   ReleaseDataBase
!^L
WaitForLOGIN: ...
&  Repeat
       Syscall #0,SyscallKillProof$ \ ! Prevent escape via ^C^C
       Print
       Print SystemID$
       Print "at ";time$;" ";date$
       If Error When
           Syscall #0,CCEchoEnable$
           len(EnteredID$)=maxlen(EnteredID$)
           Input "User ID: " EnteredID$[1,len(EnteredID$)]
           Syscall #0,CCNoEcho$
           len(EnteredPassword$)=maxlen(EnteredPassword$)
           Input "Password: " EnteredPassword$[1,len(EnteredPassword$)]
       Then
           Print "Illegal User ID/Password. Try again."
       Else
           If Error When
              SetupToModifyDatabase
           Then Print Err \ ! and loop, preventing user from logging in
           Else
               ! Scan thru User records, looking for a match
               For i=1 to NumberOfUserIDs
                   ! Read next user record
                   Read #1,UserID$,UserPassword$,...
&                          KillEnableFlag$,UserInitialProgram$
                   If UserID$=EnteredID$ and UserPassword$=EnteredPassword$
                   Then SuccessfulLogin \ ! found matching ID and password
               Next i
               ! Failed to match any valid user record.
               Print "No such UserID/Password combination, try again in 30 seconds."
               ! Note failed attempt in data file for inspection by manager.
               Syscall #1,CCPositionToEnd$
               Print #1,"Failed LOGIN: ";date$;" ";time$;" ";...
&                       EnteredID$;" ";EnteredPassword$
               ReleaseDataBase
               ! Make repeated attempts to crack the system dreadfully slow.
               Syscall SyscallDelayThirtySeconds$
           Fi
       Fi
   End \ ! Repeat until successful login
!^L
SuccessfulLogin: ! Matching UserID and password have been found.
   ! Record successful login in the data file.
   Syscall #1,CCPositionToEnd$
   Print #1,"LOGIN  ";date$;" ";time$;" ";EnteredID$
   ReleaseDataBase

   Syscall #0,CCEchoEnable$ \ ! which was disabled for the password

   If Error When Open #1,"LOGOFF.NEWS"
   Then
       If Err=1011
       Then ! No news is no news. Don't print anything.
       Else Error \ ! Croak die....
   Else
       Repeat
            ! Print the contents of the NEWS file.
            Input #1,Line$
            If not(EOF(1)) Then Print Line$
       Unless EOF(1) End
       Close #1 \ ! just to be nice
   Fi
   If KillEnableFlag$="Y" Then Syscall SyscallKillEnable$
   Chain UserInitialProgram$ \ ! Pass control to user-specific program
!^L
PrintSupportModeMenu: ! Print out menu so user knows what he can do
   Print
   Print "     LOGOFF support mode commands"
   Print
   Print "add <USERID>"
   Print "delete <USERID>"
   Print "printuserinfo <FILENAME>"
   Print "printaccesslog <FILENAME>"
   Print "resetaccesslog"
   Print "changesystemid"
   Print "changesupportpassword"
   Print "help"
   Print "exit"
   Print
AskCommand: ! Ask user what he wants to do
   Input "LOGOFF>" Line$
   If find(lowercase$(line$),"add ")=1 Then AddUserID
   If find(lowercase$(line$),"delete ")=1 Then DeleteUserID
   If find(lowercase$(line$),"printuserinfo")=1 Then PrintUserInfo
   If find(lowercase$(line$),"printaccesslog")=1 Then PrintAccessLog
   If find(lowercase$(line$),"resetaccesslog")=1 Then ResetAccessLog
   If find(lowercase$(line$),"changesystemid")=1 Then ChangeSystemID
   If find(lowercase$(line$),"changesupportpassword")=1
   Then ChangeSupportPassword
   If find(lowercase$(line$),"help")=1 Then PrintSupportModeMenu
   If find(lowercase$(line$),"exit")=1 Then Exit
   Print "?? Illegal LOGOFF support command."
   Goto PrintSupportModeMenu
!^L
AddUserID: ! Wants to add another user ID
   let line$=lowercase$(line$)
   let len(EnteredID$)=maxlen(EnteredID$)
   let EnteredID$[1,len(EnteredID$)]=right$(line$,5)
   let len(EnteredPassword$)=maxlen(EnteredPassword$)
   Input "Password: " EnteredPassword$[1,len(EnteredPassword$)]
   let EnteredPassword$=lowercase$(EnteredPassword$)
   Repeat
       ! Ask whether to kill enable after LOGIN or not.
       Input "Kill Enable after login (Y/N)? " EnteredKillEnableFlag$
       EnteredKillEnableFlag$=UpperCase$(EnteredKillEnableFlag$)
   Unless EnteredKillEnableFlag$="Y" or EnteredKillEnableFlag$="N" End
   len(EnteredUserInitialProgram$)=maxlen(EnteredUserInitialProgram$)
   Input "Initial program to run? " ...
&        EnteredUserInitialProgram$[1,len(EnteredUserInitialProgram$)]
   SetupToModifyDataBase
   For i=1 to NumberOfUserIDs
       ! Read next user record
       Read #1,UserID$,UserPassword$,...
&              KillEnableFlag$,UserInitialProgram$
       If UserID$=EnteredID$
       Then
           Print "User ID already exists, can't add."
           ReleaseDataBase
           Goto AskCommand
       Fi
   Next i
   Read #1@(i-1)*UserRecordSize+UserRecordBase,OldUserRecord$ \ ! make room for new record
   ! Now shuffle tail end of file up one User record's worth
   Repeat
      i=i+1
      Read #1@(i-1)*UserRecordSize+UserRecordBase,UserRecord$ \ ! make room for OldUserRecord$
      Write #1@(i-1)*UserRecordSize+UserRecordBase,OldUserRecord$
      OldUserRecord$=UserRecord$
   Unless len(UserRecord$)=0 End
   ! Add new UserID record
   Write #1@NumberOfUserIDs*UserRecordSize+UserRecordBase,...
&           EnteredID$,EnteredPassword$,...
&           EnteredKillEnableFlag$,EnteredUserInitialProgram$
   NumberOfUserIDs=NumberOfUserIDs+1 \ ! bump # User ID records and record
   Write #1@0,LogoffInterlock$,SystemID$,SupportPassword$,NumberOfUserIDs
   ReleaseDataBase
   Goto AskCommand
!^L
DeleteUserID: ! Wants to remove a user ID
   let line$=lowercase$(line$)
   let len(EnteredID$)=maxlen(EnteredID$)
   let EnteredID$[1,len(EnteredID$)]=right$(line$,8)
   SetupToModifyDataBase
   For i=1 to NumberOfUserIDs
       ! Read next user record
       Read #1,UserID$,UserPassword$,...
&              KillEnableFlag$,UserInitialProgram$
       If UserID$=EnteredID$
       Then
           ! Found ID of record to delete
           ! Replace record to be deleted with last User Record
           Read #1@(NumberOfUserIDs-1)*UserRecordSize+UserRecordBase,UserRecord$
           Write #1@(i-1)*UserRecordSize+UserRecordBase,UserRecord$
           NumberOfUserIDs=NumberOfUserIDs-1 \ ! shrink record count
           Write #1@0,LogoffInterlock$,SystemID$,...
&                     SupportPassword$,NumberOfUserIDs
           ! Now shuffle end of file down by one record
           i=NumberOfUserIDs+1 \ ! where to start shuffling
           Repeat
               Read #1@i*UserRecordSize+UserRecordBase,UserRecord$
               Write #1@(i-1)*UserRecordSize+UserRecordBase,UserRecord$
               If len(UserRecord$)<>maxlen(UserRecord$)
               Then
                   ! All done shuffling
                   Syscall #1,CCSetFileSize$ \ ! Chop file off here
                   ReleaseDataBase
                   Goto AskCommand
               Fi
               i=i+1
           End
       Fi
   Next i
   Print "No such user ID."
   ReleaseDataBase
   Goto AskCommand
!^L
PrintUserInfo: ! Wants to print out all existing user IDs
   if len(line$)>13
   then Create #2,right$(line$,15)
   else Create #2,"console:"
   LockDataBase
   Print #2,"    User ID         Password     KE     Initial Program"
   !        "123456789112345678921234567893123456789412345678951234567896"
   For i=1 to NumberOfUserIDs
       ! Read next user record
       Read #1,UserID$,UserPassword$,...
&              KillEnableFlag$,UserInitialProgram$
       Print #2,UserID$;" ";Userpassword$;" ";...
&               KillEnableFlag$;"   ";UserInitialProgram$
   Next i
   ReleaseDataBase
   close #2
   Goto AskCommand

PrintAccessLog: ! Wants to print out access log
   if len(line$)>15
   then create #2,right$(line$,16)
   else create #2,"console:"
   LockDataBase
   Position #1@NumberOfUserIDs*UserRecordSize+UserRecordBase
   Repeat
       Input #1,Line$ \ ! Fetch access log line
       If not(EOF(1)) Then Print #2,Line$ \ ! Print to output file
   Unless EOF(1) End
   ReleaseDataBase
   close #2
   Goto AskCommand

ResetAccessLog: ! Wants to reset access log to empty
   SetupToModifyDataBase
   Position #1@NumberOfUserIDs*UserRecordSize+UserRecordBase
   Syscall #1,CCSetFileSize$ \ ! Chop file just past User records
   ReleaseDataBase
   Goto AskCommand
!^L
ChangeSystemID: ! Wants to change SystemID
   Input "New System ID: " Line$
   SetupToModifyDataBase
   len(SystemID$)=maxlen(SystemID$)
   SystemID$[1,len(SystemID$)]=Line$ \ ! Blank fill
   Write #1@0,LogoffInterlock$,SystemID$,SupportPassword$,NumberOfUserIDs
   ReleaseDataBase
   Goto AskCommand

ChangeSupportPassword: ! Wants to change support password
   Input "New Support Password: " Line$
   SetupToModifyDataBase
   len(SupportPassword$)=maxlen(SupportPassword$)
   SupportPassword$[1,len(SupportPassword$)]=lowercase$(line$)
   Write #1@0,LogoffInterlock$,SystemID$,SupportPassword$,NumberOfUserIDs
   ReleaseDataBase
   Goto AskCommand

END
