[1,100]SCLI.PDL -- Program Descriptor Language for Security CLI Author: Jim Bostwick 24-JAN-1985 13:27:45 History: Randy Baldwin 7-FEB-1985 09:44:29 Adding more info. JMB 20-FEB-1985 Added MACRO subroutine headers, variable name mapping. All MACRO stuff is surrounded by ;*+ ... ;*- pairs. Last Edit: 25-FEB-1985 15:28:00 This file contains a pseudocode program description of the security CLI system. This design approach is for the author experimental. It is modeled after Pascal, somewhat loosely. PDL: PDL (alias piddle) is a pseudocode language intended for design application. Modeled loosely after Pascal, PDL replaces traditional flowcharting, data dictionaries (for non-DB intensive applications) and in general streamlines the design process. PDL adheres to Pascal in the following ways: * All Pascal keywords are recognized, including the := assignment construct. * Indentation is used to indicate statement nesting. PDL does not adhere to Pascal in the following: * Unlike Pascal, PDL is strictly top-down (more or less reverse of Pascal). However, Variables and data are still declared at beginning in conformance with top-down rules. * Variables, tests, etc. may be English phrases. * Typing is not enforced - it is encouraged. * Intermediate variables are not required -- "REPEAT check-record UNTIL no-more-records" is perfectly legal. PROGRAM SCLI -- Command line interpreter for Dial-in security. { Description: SCLI is a CLI which is (by various means) made the active CLI for all non-logged-on Dial-up terminals. (Although intended for dial-up terminals, SCLI may be installed on local terminals as well. ) SCLI enforces an additional layer of access protection on those terminals under its control. Potential users of the system must first deal with and satisfy SCLI before being allowed to enter the normal RSX logon procedure. Note that users must posess access information (passwords) for BOTH SCLI and RSX (Hello) in order to gain access to the system. SCLI also detects and counteracts most attempts to break through itself. It does this by entering a 'mumble mode' upon detection of breakin attempts. Finally, SCLI maintains a private log of legal log-in's and illegal attempts. Information in the log does NOT reflect SCLI's internal mechanism, making the log of very limited use in attempting to circumvent SCLI. } EXTERNALS: GCCI -- get command for command interpreter (SYSLIB) MRKT -- mark time (SYSLIB) CLEF -- clear event flag SETF -- set event flag STOP -- stop self (SYSLIB) EXIT -- exit (SYSLIB) DATA: command_record = RECORD command_name : ^string; { input string (encrypted) } reply_pointer: ^string; { pointer to reply string - may be nill } { for passwords, reply-string is user ID } user-id: ^string; { plain-text user ID for logging uses } end; { note on vectors: SCLI can be built to protect any number of terminals. For each protected terminal, an entry will exist in several vectors. These are defined below in terms of constant Num_tts. } Num_tts = xxx; VAR Mumble_mode: array[num_tts] of boolean; Cont_mumble: array[num_tts] of boolean; Foo_count: array[num_tts] of integer; Mumble_efn: array[num_tts] of event_flag; { maps tt to EFN number } cmdrec: comman_record; command_table: array[num_commands] of command_record; {@MARK@} {------------------------- Main SCLI ----------------------------} BEGIN { SCLI } Init; { initialize CLI } Repeat Reply-ptr := NIL; GCCI(command_line) { obtain command line} IF system_message THEN Do_Sys_message(sys_message) { system message sent to cli } ELSE IF command_line THEN BEGIN Encrypt({in}command_line; {out} crypt_command); Look_up_command({in}crypt_command; {out} cmdrec); if mumble_mode[tt] then BEGIN increment foo-count[tt]; cont_mumble[tt] := true; reply-ptr := random-reply END else if cmdrec.pass then BEGIN set_cli_mcr; log(logged-on,tt); reply-ptr := cmdrec.reply; foo-cnt[tt] := 0 END else BEGIN increment foo-count[tt]; if foo-count[tt] > max-foo then begin-mumble; IF cmdrec.reply <> NIL THEN reply-ptr := cmdrec.reply ELSE reply-ptr := random-reply END; if reply-ptr <> NIL then type(reply-ptr); END; UNTIL true; {repeat forever} STOP {system will unstop} END. {------------------------- Do_Sys_Message -----------------------} Procedure Do-Sys-Message({in}cmdptr:^command-line); { This procedure will take a system message and process it. Most cases simply log the action and others will act on the message. See System Management guide chapter 11 for system message definitions. } BEGIN CASE Sys_Message of Init/Enable, Init/Disable: (LOG,SCLI_initialized) Enable: (LOG, SCLI_enabled) Disable: (LOG, SCLI_disabled) Eliminate: BEGIN (LOG, SCLI_elim_attempt) (Reestablish_SCLI) END Eliminate_All: (LOG, SCLI_eliminate) Terminal_Linked: (LOG, SCLI_linked, terminal) Terminal_Released: (LOG, SCLI_release, terminal) Special: {user initiated cli message} BEGIN CASE Special of Eliminate: (LOG, SCLI_priv_elim) END {case} END {case} END { do_sys_message } {------------------------- Look_Up_Command ----------------------} Procedure Look_up_Command({in}crypt-command:string; {out}cmdptr:^command-record) { The function of this procedure is to take an encrypted command and search the command table for a match. It will return a pointer into command table. If the command is not found in the table, the pointer to the last entry (illegal) will be returned unconditionally. } BEGIN x := 1; REPEAT IF table[x] = crypt_command THEN BEGIN match = true; END ELSE x := suc(x); UNTIL (x > max_table_entries) OR (match); cmdrec := ref(command_table[x]); END; {------------------------- Reestablish_CLI ----------------------} Procedure Reestablish_CLI; { This procedure will reestablish the SCLI for a tt in the event of it's elimination via a CLI /ELIM=cliname command. This prevents a user from simply eliminating the CLI in an attempt to bypass it. It works because SCLI receives a message informing it of the elmination attempt. NOTE: if this wont' work, just leave terminal slaved. } BEGIN spawn(MCR, UPCLI); { task to bring up the CLI again } { UPCLI is MCR command needed to setup cli } IF not success then LOG(up_error) END {------------------------- Encrypt ------------------------------} { Encrypt takes the command line returned by GCCI, locates it's real length (less trailing whitespace), and encrypts it in place (via a call to CRYPT). CALLS: CRYPT } Local data: length: word; {number of words to crypt } BEGIN { Encrypt } find end of command_line length := length of command line {in bytes} call CRYPT(cmd_ptr,length); END; { Encrypt } {------------------------- LOG ----------------------------------} { The LOG function is used to send SYSTEM SERVICE MESSAGES to the LB:[1,6]LOG.ERR file. These messages allow a knowledgable person to monitor SCLI's activity. } CONST INIT = 'Initialized' ENABLE = 'Enabled' DISABLE = 'Disabled' ATTEMPT = 'Elimination Attemted' ALL_ELIM = 'System Elimination' LINKED = 'Dev xxxx linked' RELEASE = 'Terminal xxxx released' PRIV_ELIM = 'Priveleged Elimination' MUMBLE = 'Invalid detected' UP_ERROR = 'Restart error' WHO = 'SCLI: ' BEGIN buffer := who; buffer := buffer + message_type IF tt: available THEN buffer := buffer + tt_name SEND_SYSTEM_MESSAGE; { via smsg directive } END {------------------------- Begin_mumble -------------------------} { Begin-mumble initiates mumble-mode. In this mode, messages are somewhat different, but most importantly, all keys are illegal. That is, no input whatsoever will cause a fall-through to MCR. Mumble-mode may only be teminated by expiration of the mumble timer, or by aborting SCLI. } BEGIN {Begin_mumble} mumble_mode[tt] := true; MRKT(mumble_time,mumble_efn[tt],mumble_AST); {set timer} cont_mumble[tt] := false; {flag to show activity during mumble time } log(mumble_initiated, terminal) end; {------------------------- end_mumble -------------------------} { End-mumble terminates mumble-mode processing. SCLI will now accept valid passwords as commands to fall-through to MCR. } BEGIN mumble_mode[tt] := false; foo_count[tt] := 0; END; {------------------------- Mumble_AST ---------------------------} { Mumble-AST is the AST entry point for the mumble-timer. If cont-mumble is true, the mumble timer is restarted, and we remain in mumble mode. IF cont-mumble is false, we call end-mumble, restoring normal operations. } BEGIN {mumble_AST } IF cont_mumble THEN BEGIN MRKT(mumble_time,mumble_efn[tt],mumble_AST) cont_mumble[tt] := false END ELSE end_mumble(tt) END; {------------------------- TYPE ---------------------------------} Procedure Type({in} ptr:^string; {in} length:integer); { This procedure prints an ascii or asciz string on LUN 6 (normally the users ti: ). If length=0 on input, asciz is assumed, and TYPE calls SLEN to determine the length of the string (null terminated). }