{ +---------------------------------------------------------------------------+ | | | [WHO]WHO.PAS Provide Information About System Users | | Scott Bailey, Xerox Corporation Created July 18, 1983 | | | | Modification History | | 7-MAR-1991 (RSB) Add /SYMBOL support, allow /NOOUTPUT | | 23-JAN-1991 (RSB) Add /FULL display support | | 8-NOV-1990 (RSB) Restore paranoia for /DATABASE and /STATISTICS | | 26-OCT-1990 (RSB) Ported from Fortran to Pascal; added class support; | | (significant internal reorganization...) | | 26-FEB-1990 (RSB) Fix search logic for [*,mmm] matching duplicate UICs | | 21-DEC-1989 (RSB) Add hooks for VMS V5.2 flag support in MASKUTIL et al | | 31-AUG-1988 (RSB) Add support for MANAGER output field | | 29-JAN-1988 (RSB) Add support for /FLAG /PRIVILEGE /DEFPRIVILEGE (yuck) | | 27-OCT-1987 (RSB) Allow WHO_SHOWALL to indicate wildcards are okay | | 26-OCT-1987 (RSB) Add support for /STATISTICS qualifier | | 13-FEB-1987 (RSB) Implement check for WHO_SHOWALL logical name | | 25-SEP-1986 (RSB) Make priority formatting UB instead of ZB | | 27-AUG-1986 (RSB) Make sure we only display user records | | 22-JUL-1986 (RSB) Fix GRPPRV determination logic to use valid mask | | 10-JUL-1986 (RSB) Finally add local field support | | 7-JUL-1986 (RSB) More output fields... | | 2-JUL-1986 (RSB) Add another 10 output fields | | 1-JUL-1986 (RSB) Add more output fields | | 24-JUN-1986 (RSB) Add additional output fields | | 20-JUN-1986 (RSB) Fix cutoff logic for wildcarded usernames | | 4-JUN-1986 (RSB) Massive internal hacks for security, output formatting | | (read as "90% rewrite performed") | | 16-MAY-1986 (RSB) Use RTL to do wildcarding more efficiently | | 16-JAN-1985 (RSB) Adapted for VMS V4.0; support long UICs & identifiers | | 22-AUG-1984 (RSB) Add USAGE to /SHOW= to allow disk usage displays | | 4-JUL-1984 (RSB) Extend quota checks to use various encoding formats | | 18-MAY-1984 (RSB) Use logical name SYSUAF to locate authorization file | | 17-NOV-1983 (RSB) Allow owner name matching and add /MATCH qualifier | | 29-JUL-1983 (RSB) Use quota files instead of SYSUAF for quota info | | 25-JUL-1983 (RSB) Fix output generating and wildcard comparison logic | | | +---------------------------------------------------------------------------+ This program generates selective authorization listings using several search criteria. It allows the resulting listing to be formatted according to the user's specifications and directed to the output device or a file. Assuming the program is installed with privilege, the following security restrictions are enforced for all users: 1. Program privileges are used only for the purpose of gaining access to the authorization database. They are disabled at all other times. 2. Normal VMS UIC access checking is used to determine the visibility of all records in the authorization database. That is, a record is visible if: o The user has SYSPRV, BYPASS, READALL or a system UIC group number; o The user has GRPPRV and current group number matches the record's group; o The user's current UIC matches the record's UIC. 3. Additionally, a record is visible if th user's default UIC holds an identifier of the form "x_MANAGER" and "x" is the contents of the record's class field (which is defined in the record's local data area.) 4. Additionally, a record is visible if ALL of the following are true: o The record's UIC group number is at least 100 (octal); and o The record's UIC holds an identifier of the form "CAN_LOGIN_ON_x"; and o The user's default UIC holds a corresponding "xSNOOP" identifier. 5. Additionally, if the logical name WHO_SHOWALL is defined in the system name table at executive mode, then any record is visible to any user as long as a wildcard search method is not used. 6. However, if the above logical name translates to "WILDCARDS" then there are no visibility restrictions of any kind for any user. } [ident('V5.4-004'), { Match major/minor to VMS } inherit('sys$library:starlet', { System service definitions } 'sys$library:pascal$lib_routines', { Run-time library routines } 'whodefs', { Common definitions } 'uafdef', { UAF record definitions } 'search', { Parse/Search routines } 'access', { Visibility rules } 'statistics', { Statistics reporting } 'display')] { Output formatting procedures } program who(sysuaf,listing); const default_class = 'SYSTEM_DEFAULT '; var sysuaf : authorization_file; { User authorization file } listing : text; { Output file } stat : integer; { Return call status } use_statistics : boolean; { Should we report stats? } dcl_symbol, { DCL output symbol name } input_file, { Authorization DB name } output_file : varying [132] of char; { Output file specification } targets : search_pointer; { List of target names } scanned, displayed : integer value 0; { UAF records processed } use_symbol, { Write DCL symbol? } use_output, { Write output file? } nice, { Is this a friendly record? } continue_search : boolean; { Keep checking vs. target? } { This routine displays the selected record if the user is allowed to see it. } procedure write_current_record; var buff : varying [256] of char; { Output buffer } use_class : ascii_32; { Class for access checks } uaf_local : ^xrx_uaf_type; { Pointer to local area } begin {procedure write_current_record} { First we must find the local data area and verify that it is usable. We need the class field from it to perform the access checks. } use_class := default_class; { Assume we'll use default } if sysuaf^.usrdatoff <> 0 then begin { If local area exists } uaf_local::unsigned := iaddress(sysuaf^) + sysuaf^.usrdatoff; if (uaf_local^.length = xrx$c_size) { If length is correct } and (uaf_local^.version = xrx$c_version) then { and version is correct } use_class := uaf_local^.class; { Use record's class } end; {then} { Do the output if the record is visible } if visible(sysuaf^.uic,use_class,targets^.kind) then begin displayed := displayed + 1; { Another record output } { If this is the first record written, check for headers. (If we are using a file) } if (use_output and (displayed = 1)) and_then format_header(buff,1) then begin writeln(listing,buff); { Write headers } format_header(buff,2); writeln(listing,buff); end; {then} { Format and output the UAF data } format_record(sysuaf^,listing,use_output,dcl_symbol,use_symbol); end; {then} end; {procedure write_current_record} begin {program who} { Disable any privileges owned by program which user doesn't already have enabled. } minimize_privileges; { Decide if we'll be reporting on statistics later. If we are, get initial stats. } stat := cli$present('STATISTICS'); use_statistics := ((stat = cli$_present) or (stat = cli$_defaulted)); if use_statistics then collect_initial_statistics; { Figure out what we're looking for and what we'll be displaying. } generate_format; { Determine output format } parse_input(targets); { Get user specifications } { See if we are supposed to write an output file, and what it is. } stat := cli$present('OUTPUT'); use_output := (stat = cli$_present) or (stat = cli$_defaulted); if use_output then begin cli$get_value('OUTPUT',output_file.body,output_file.length); { Open the output file. With privileges disabled above, this will proceed just as if the user were trying this from any unprivileged image. } open(file_variable := listing, file_name := output_file, default := 'SYS$DISK:[]WHO.LIS;', history := NEW, record_length := 255); rewrite(listing); end; {then} { See if output is supposed to go to a DCL symbol, and retrieve its name. } stat := cli$present('SYMBOL'); use_symbol := (stat = cli$_present) or (stat = cli$_defaulted); if use_symbol then cli$get_value('SYMBOL',dcl_symbol.body,dcl_symbol.length); { Tell the user (and abort) if results won't be captured anywhere. } if not (use_symbol or use_output) then lib$signal(%ref who$_nooutput); { If the user wants to see a specific database, retrieve its name. } stat := cli$present('DATABASE'); if (stat = cli$_present) or (stat = cli$_defaulted) then cli$get_value('DATABASE',input_file.body,input_file.length) else input_file := ''; { Open the target database for lookups. Note that the special useropen routine will help to open the file securely if no name was specified above. } open( file_variable := sysuaf, file_name := input_file, default := 'SYSUAF', history := READONLY, access_method := KEYED, organization := INDEXED, sharing := READWRITE, user_action := safe_file_open); reset(sysuaf); { Enter main search loop here. Find and display the first record matching the current search rule. } while targets <> nil do begin { While targets remain } if position_file(sysuaf,targets^) then begin { Grab first likely record } scanned := scanned + 1; { Another record touched } if matched(sysuaf,targets^,continue_search) then { Good record? } write_current_record; { If so, "display" it } { Now continue scanning as long as there is a possibility of finding more matches for the current search rule. } while continue_search do if find_next(sysuaf,targets^) then begin { Get next potential record } scanned := scanned + 1; if matched(sysuaf,targets^,continue_search) then write_current_record; end else continue_search := false; { Stop trying if scan fails } end; {then} { That finishes off that specification. Get the next and start over again. } targets := targets^.link; end; {while} { We've finished with files, they can be closed. Also, display statistics if they were requested. Note that for displayed statistics, we lie about the number of records scanned if we were restricting access by this user. } close(sysuaf); if use_output then close(listing); if use_statistics then begin if be_careful then { If user was nonprived } scanned := displayed; { Hide actual record count } report_statistics(scanned,displayed); { Print information } end; {then} { Finally, exit nicely if we've output some records, or semi-nicely if we didn't find anything. (At least anything we could display :-) } if displayed > 0 then $exit(ss$_normal) else begin lib$signal(%ref who$_notfound); { Make sure message appears } $exit(%ref who$_notfound); { and match exit status to it } end; {else} end. {program who}