TCL Command Wrapper:
In my opinion there are some commands that should not be readily accessible to most users. Speaking from experience, I have seen users create a new temporary file pointer to the current directory (a copy of &UFD&) and then later run CLEAR.FILE on the temporary file (clearing the current directory (the UniVerse account))! This example is an unusual case which is unlikely to be repeated (especially by the programmer I saw do it). However I have also seen users run CLEAR.FILE on an important application file by mistake. I have never personally seen anyone delete or clear a file accidentally when they forgot there was an active select list but I can certainly see where this could happen.Another example of misusing a command is someone changing data with the editor, essentially circumventing the application. Depending on the application this could have unintended consequences (for example, possibly bypassing security and auditing, or programmatic relational cross references, etc.)
One way to avoid potential problems is to restrict access to certain commands. Or at the very least record individual use of certain commands. This article suggests employing a "wrapper" to restrict access to and record usage of certain commands.
- Note: If the goal is strictly to restrict access then I suggest setting up important commands as VOC (R)emote items, with a program (defined in field four of the VOC remote record) that returns a one in the 'flag' (the last) argument in the security subroutine if access is granted. Review "UniVerse System Description", pg 3-15 (v11.2.5, July 2015, UNV-1125-SYS-1) for more information regarding the Remote command VOC type.
The least innocuous step is to audit the use of commands. I propose creating a cataloged program that would replace the VOC verb (or sentence, paragraph, etc.) of commands that should be audited. This replacement program would determine the "native" command to run by using the command name from the sentence that invoked the wrapper. This native command, if only for delivered verbs, could read NEWACC for the verb contents and reconstruct a new verb to run. However, the idea is for the wrapper to also work for non-delivered, custom sentences, paragraphs, and verbs. With this in mind I propose copying the verb to-be-wrapped to the VOC but with a unique and well known name. In this example the program assumes the native VOC item has .CMDWRPR appended to it and is in the VOC.
Note: All programming displayed on this site is for illustrative purposes only. Use at your own risk.
program CMD.WRAPPER
*
*******************************************************************
*
* Put a wrapper around specific Verbs/programs.
*
*******************************************************************
*
* If the command being executed calls this program, then the verb (or program) is meant
* to be "wrapped". This means that an audit is to be made and/or a security check is to
* be performed to ensure the user is authorized to run the command.
*
* For example, The following shows that the CLEAR.FILE and DELETE.FILE verbs are to be
* wrapped (the records just have to exist, there is no sentinel that is required).
* >SORT CMDWRPR_AUDIT COL.SUP
* CLEAR.FILE
* DELETE.FILE
*
* 2 records listed.
*
* >ED CMDWRPR_AUDIT *
* SELECTing all records in the file.
*
* SELECTed record name = "DELETE.FILE".
* 0 lines long.
*
* ----: N
*
* SELECTed record name = "CLEAR.FILE".
* 0 lines long.
*
* ----: N
*
* If auditing of the command is specified then a simple date/timestamp and user information
* (ID and DBMS#) is stored in the CMDWRPR_AUDIT file. (I had thought about simply starting
* a COMO log but that may not survive long term (due to house cleaning, etc.))
*
* If the command EXISTS in the CMDWRPR_SECURITY file then the list of valid userIDs is read
* from the CMDWRPR_SECURITY file (with command as the key).
*
* For example, the following two records (CLEAR.FILE and ED) have no users and three
* users that are able to execute these commands:
*
* >SORT CMDWRPR_SECURITY @ID FMT "18L" ID.SUP EVAL "@RECORD" FMT "99L"
*
* CMDWRPR_SECURITY. CLEAR.FILE
* @RECORD.......... * Add users to this record to be able to run this command
*
* CMDWRPR_SECURITY. ED
* @RECORD.......... * Add users to this record to be able to run this command
* . brianp
* . bsmith
* . johnd
*
* 2 records listed.
*
*******************************************************************
*
* Notes:
* The "real" command is stored in the VOC with a different ID (an option could be to read
* NEWACC for the delivered command but that won't work because this program will wrap user
* custom paragraphs and sentences as well).
*
* There are on-error catches and else clauses for the audit file update. Depending on a
* flag that is hard coded in this program, the command being audited will either NOT
* be executed, or the errors will be informational only (output to the user) and the
* command will still run.
*
* NOWRAPPER on the command line will drop through and run the command without the wrapper.
*
*******************************************************************
*
equ true to 1
equ false to 0
equ IK$SLACTIVE TO 1
audit.write.failure.will.abort = true
*
* Parse command-line for the command...
*
sentence = upcase(trim(@SENTENCE))
convert " " to @FM in sentence
command = sentence<1>
sentence = delete(sentence,1,0,0) ;* remove the verb
original.sentence = sentence ;* Need this for later
locate "NOWRAPPER" in sentence<1> setting dummy then
*
* This provides for a failsafe in case the program does something unexpected
* and support staff really needs to perform what was typed/entered at TCL.
*
command := ".CMDWRPR" ;* original verb, sentence or paragraph
execute command:" ":original.sentence
end else
open '','CMDWRPR_AUDIT' to F.AUDIT then
call file.io.sub("R", F.AUDIT, command, dummy, errcode)
if not(error code) and len(dummy) then ;* Audit this command...
*
* Check for active select list...
*
audit.info = @DATE
audit.info<-1> = @TIME
audit.info<-1> = @LOGNAME
audit.info<-1> = @USERNO
audit.info<-1> = @TTY
audit.info<-1> = @LEVEL
audit.info<-1> = command:" ":original.sentence ;* This command
audit.info<-1> = @COMMAND.STACK<2> ;* Previous command
if selectinfo(0,IK$SLACTIVE) then
asl = 1
end else asl = 0
audit.info<-1> = asl ;* indicate if there is an active select list
tmp = @DATE:".":@TIME:".":@USERNO
audit.key = tmp
loop.cnt = 0
audit.updated = false
loop
loop.cnt += 1
readu dummy from F.AUDIT, audit.key locked
audit.key = tmp:".":loop.cnt
end else
write audit.info to F.AUDIT, audit.key on error
crt "Write to audit failed!"
if audit.write.failure.will.abort then stop ;* stop immediately
end else
crt "Unable to write to audit!"
if audit.write.failure.will.abort then stop ;* stop immediately
end
audit.updated = true ;* Even if there is some type of error just continue
end
until audit.updated or loop.cnt >= 100
repeat
if not(audit.updated) then
crt "Command not audited!"
if audit.write.failure.will.abort then stop ;* stop immediately
end
end else null ;* Command is NOT to be audited
end else
crt "Unable to open AUDIT! Command not audited!"
if audit.write.failure.will.abort then stop ;* stop immediately
end
end
okay.to.run = true
open '','CMDWRPR_SECURITY' to F.SECURITY then
okay.to.run = false ;* Reset to false
read security.rec from F.SECURITY, command
on error print "Unknown error reading CMDWRPR_SECURITY!"
then ;* Command IS locked down
locate @LOGNAME in security.rec<1> setting dummy then
*
* User is in the list to be granted access...
*
okay.ro.run = true
end else
*
* User is NOT in the list to be granted access...
*
okay.to.run = false
crt "Access to ":command:" is restricted."
end
end else null ;* Command is NOT locked down
end else null ;* No commands are locked down
if okay.to.run then
*
* Run the command (that is stored with the .CMDWRPR suffix)...
*
*
newcmd = trim(command:".CMDWRPR ":original.sentence)
execute newcmd
end
end
I just wrote this routine and have not extensively tested it. Please use at your own risk!
No comments:
Post a Comment