VMS Privilege Masks
by
Hunter Goatley
Clyde Digital Systems
The first DEC computer I used (indeed, the first COMPUTER I used) was a PDP 11/45. Under the RSTS operating system, privileged accounts were just that: privileged. The account could be used to do anything that required privilege. For non-privileged accounts to perform privileged tasks, the protection code on a program could be set in such a way that all privileges were given to an account while it was running the program.
I was a lab assistant at my school when we got our first VAX. No one knew much about it, but when the managers got it up and running, we lab assistants had access to an account that had limited privileges. Limited? How was that possible? I thought the account was either privileged or it wasn’t! And a whole new universe opened itself up to me…
OK, enough of dramatics. Still, the idea of having VAX accounts that could perform some privileged functions without being able to do everything was great. Gradually I understood how privileges were dispensed and how they were used to control access to privileged functions.
DEFAULT and AUTHORIZED Privileges
Privileges for each account on a VMS system are usually determined by the system manager or security officer for a site. Typically, the tasks any given account will be used for are analyzed to determine which privileges, if any, the account will need. The various privileges and the functions they allow are described in the VMS Manual “Guide to VAX/VMS System Security” for VAX/VMS V4.x.
Privileges are assigned to accounts using the AUTHORIZE program. There are two sets of privileges each account may possess: authorized privileges and default privileges. Both sets of privileges are stored in each user’s SYSUAF record in SYS$SYSTEM:SYSUAF.DAT.
As the name implies, the authorized privileges are those privileges that a user is authorized by the system manager to enable at any time. This authorized set defines all privileges that can be enabled using either the DCL command SET PROCESS/PRIVILEGE or the system service $SETPRV. This list of privileges is checked each time a request to enable privileges is made; if the privilege requested is not in this list, the privilege is not granted to the process.
The default privileges are those privileges that are automatically enabled when a process is created. For non-privileged accounts, this set usually matches the authorized set and consists of only two privileges, NETMBX and TMPMBX. These privileges allow a process to spawn a subprocess and to access files through DECnet. For more privileged accounts, the default set is usually a subset of the authorized privileges. Since power is dangerous, an account’s default privileges are usually the less powerful privileges (such as TMPMBX and NETMBX). If users want to take advantage of their greater privileges, they must specifically enable them, helping to reduce the chances of accidents caused by too many privileges.
It is possible for a privilege to be part of the default set, but not be an authorized privilege. This implies that, though the privilege may be used, it cannot be re-enabled if disabled (once the privileged is turned off, it is lost for that process).
Enabling and Disabling Privileges
There are three ways a process can enable and disable privileges: the DCL command SET PROCESS, the DCL lexical function F$SETPRV, and the system service $SETPRV. Of course, the DCL command and lexical function ultimately call the $SETPRV system service.
The simplest way to turn off or on privileges is to use the SET PROCESS command at the DCL prompt. The command format is simple:
$ SET PROCESS/PRIVILEGE=OPER $ SET PROCESS/PRIVILEGE=(NOOPER,SYSPRV)
When this command is used to enable or disable a privilege, the change is permanent (i.e., the privilege is enabled or disabled for the life of the process unless the SET command is used again).
Programs can temporarily turn off and on privileges if necessary. The change is considered temporary because it is only in effect until image rundown. A temporary change is made by calling $SETPRV, passing 0 in as the PRMFLG parameter.
VMS Privileges and Descriptions =============================== Privilege Name Description ============== =========== CMKRNL May change mode to kernel CMEXEC May change mode to exec SYSNAM May insert in system logical name table GRPNAM May insert in group logical name table ALLSPOOL May allocate spooled device DETACH May create detached processes DIAGNOSE May diagnose devices LOG_IO May do logical i/o GROUP May affect other processes in same group ACNT May suppress accounting message PRMCEB May create permanent common event clusters PRMMBX May create permanent mailbox PSWAPM May change process swap mode ALTPRI May set any priority value SETPRV May set any privilege bit TMPMBX May create temporary mailbox WORLD May affect other processes in the world MOUNT May execute mount acp function OPER Operator privilege EXQUOTA May exceed quota NETMBX May create network device VOLPRO May override volume protection PHY_IO May do physical i/o BUGCHK May make bug check log entries PRMGBL May create permanent global sections SYSGBL May create system wide global sections PFNMAP May map to specific physical pages SHMEM May create/delete objects in shared memory SYSPRV May access objects via system protection BYPASS Bypasses UIC checking SYSLCK May lock system wide resources SHARE May assign channels to non-shared device GRPPRV Group access via system protection READALL May read anything as the owner SECURITY May perform security functions
Privileged Programs
Frequently, non-privileged users need to perform routine tasks that may require a way to temporarily attain some privileges. Under the RSTS operating system, a program could be made privileged by setting its protection code to a given value. Under UNIX, a program is made privileged by setting the SU bit. Since the VMS privilege scheme is more complicated than either of these methods, a different method is employed.
Using the INSTALL utility, a system manager can install a given program with privileges, allowing users without privileges to perform privileged functions in a controlled fashion. Many of the programs run by various DCL commands are installed with privilege, including MAIL, MONITOR, and PHONE. To install a program with privileges, the system manager needs only to determine which privileges the program requires to perform its task, and then install it with commands similar to the following:
$ INSTALL :== $INSTALL/COMMAND $ INSTALL ADD SYS$SYSTEM:PRIVPROG.EXE/PRIVILEGE=CMKRNL
Once a program has been installed with privileges, all users who have execute access to the program can use it, even though they may not have the required privilege. I will discuess how this is accomplished later.
Privilege Masks
Each set of privileges used by VMS is stored in a 64-bit mask, with each bit corresponding to a privilege. When a bit is set, the corresponding privilege is enabled; when reset, the privilege is not enabled. Symbolic names for each privilege bit are defined by the $PRVDEF macro. For each privilege, there is a symbol for each bit mask and a symbol for the bit offset in a mask. For example, the symbol PRV$M_SYSPRV serves as a bit mask that can be used to OR two privileges together. The symbol PRV$V_SYSPRV is the bit number in a privilege mask that corresponds to the SYSPRV privilege.
There are 7 different privilege masks used by VMS. I will tell where each of these masks is stored, how it is used, and how a program can acquire a copy of each mask.
UAF$Q_PRIV and UAF$Q_DEF_PRIV
The masks UAF$Q_PRIV and UAF$Q_DEF_PRIV are found in the SYSUAF record for each user (the symbolic names are offsets into the UAF record). UAF$Q_PRIV points to the authorized privilege mask and UAF$Q_DEF_PRIV points to the default privilege mask. When a process is created, LOGINOUT uses these masks to initialize the privilege masks that are kept in memory for each process. A program can get a copy of each mask by calling the $GETUAI system service with the item codes UAI$_PRIV and UAI$_DEF_PRIV.
CTL$GQ_PROCPRIV
The mask of all permanently enabled privileges is stored in the Control Region for each process (P1 space) at the global location CTL$GQ_PROCPRIV. When a process is created, this mask is initialized with the contents of mask UAF$Q_DEF_PRIV. As privileges settings are modified (via the SET PROCESS command or by calling $SETPRV with the permanent flag set), this mask is updated to reflect the changes. A copy of this mask can be obtained by calling the $GETJPI system service with item code JPI$_PROCPRIV.
PHD$Q_PRIVMSK
The process header holds three privilege masks. Each of the symbols is an offset from the beginning of the process header. PHD$Q_PRIVMSK makes up the first 8 bytes of the header and is the mask for all currently enabled privileges. It is this mask that is checked by all system services that require that the caller has certain privileges. When a process is created, the PROCPRIV mask is used to initialize PRIVMSK. If an image calls the $SETPRV system service to temporarily enable or disable a privilege (the permanent flag is clear), this mask is modified to reflect the change. At image rundown (when the program exits), the contents of PROCPRIV are again copied into PRIVMSK. Privileges are temporarily enabled and disabled by resetting the mask at image rundown. The PRIVMSK is returned by $GETJPI when the item code JPI$_CURPRIV is given.
PHD$Q_AUTHPRIV
The mask PHD$Q_AUTHPRIV is a copy of the authorized privilege mask from the SYSUAF record (mask UAF$Q_PRIV). When a process tries to enable a privilege, this mask is checked to ensure that the process is authorized to do so. This mask is never changed by VMS once it has been initialized. It is returned by $GETJPI when the item code JPI$_AUTHPRIV is given. Note that it is this mask that allows a process to disable a default privilege and re-enable it later.
PHD$Q_IMAGPRIV
The IMAGPRIV mask is the mask of all privileges an installed program has. It is clear (all bits are reset) if the current image is not installed with privileges. When $SETPRV is called to enable a privilege temporarily, this mask is OR’ed with AUTHPRIV to see if the privilege can be enabled. When an installed image is activated, this mask is OR’ed with PRIVMSK, so that PRIVMSK consists of all currently enabled process privileges plus all privileges of the installed image. A copy of this mask can be obtained by calling $GETJPI with the item code JPI$_IMAGPRIV.
PCB$Q_PRIV
The mask PCB$Q_PRIV is stored in the Process Control Block for each process; it is always an exact duplicate of the PRIVMSK mask. This mask is a component of the Access Rights Block, which is currently stored in the PCB. Since it is a copy of PRIVMSK, a copy can be obtained by getting PRIVMSK.
The EXE$SETPRV Routine
The $SETPRV system service is implemented as system routine EXE$SETPRV. You can examine the code for this routine using SDA to disassemble instructions beginning at symbol EXE$SETPRV. Analysis of the code reveals a couple of security features that may not be readily apparent, in addition to extremely slick examples of fancy bit mask manipulation.
The first is that a program that is installed with privilege cannot use any of those privileges to permanently enable a privilege. One of the first things the EXE$SETPRV routine does is to get a copy of the AUTHPRIV mask. It then checks to see if PRV$V_SETPRV is set; if it is, all other checks are bypassed. If SETPRV is not an authorized privilege, EXE$SETPRV checks the permanent flag parameter. When privileges are to be enabled temporarily (for the life of the image), the IMAGPRIV mask is OR’ed with the AUTHPRIV mask, resulting in a bit mask that indicates all privileges that can be enabled. However, if the privileges are to be enabled permanently, EXE$SETPRV branches around OR’ing the IMAGPRIV mask with the AUTHPRIV mask.
What this means is that a user cannot install a program that simply calls $SETPRV to permanently enable privileges (the code doesn’t look at IMAGPRIV when the permanent flag is set). The other implication is that a program installed with SETPRV cannot be used to bypass all other privilege checks, since SETPRV must be an authorized privilege.
Analysis of the EXE$SETPRV code points to one more security issue of which many system managers/security officers may not be aware. Since VMS system code frequently calls $SETPRV from executive or kernel mode, there is a check in EXE$SETPRV that SKIPS ALL PRIVILEGE CHECKS IF THE CURRENT ACCESS MODE IS EITHER EXECUTIVE OR KERNEL. In essence, code running in executive or kernel mode may turn on any privilege (temporarily or permanently). For obvious reasons, this is not a well-publicized fact. However, I mention it to warn against granting the CMKRNL and CMEXEC privileges to just any account. Most everyone knows that CMKRNL is without question the most powerful privilege in VMS, but not everyone knows exactly how powerful CMEXEC can be.
You have been duly warned!
Programming with Privileges
.QUAD <PRV$M_SYSPRV!PRV$M_WORLD>
This assembler directive creates a quadword whose bits are set according to the result of ORing the two masks together (the bits representing SYSPRV and WORLD would both be set). The following MACRO instruction tests to see if the SYSPRV privilege is enabled in PRIV_MASK:
BBS #PRV$V_SYSPRV,PRIV_MASK,DESTINATION
If the PRIV_MASK bit numbered PRV$V_SYSPRV is set, control would pass to DESTINATION. To test for one of a group of privileges, the following instructions could be used:
BITL #<PRV$M_SYSPRV!PRV$M_WORLD>,- PRIV_MASK BNEQ LABEL ; Branch if either is set
As mentioned above, the various privilege masks for a process can be obtained by calling the $GETJPI system service. The service expects an item list to be passed in. I have included a very small program to provide an example that demonstrates how to call $GETJPI to get a process’s privileges (Program 1).
Program 1 was also included to point out a subtle aspect of the LIB$SPAWN library routine. When a subprocess is created, all privileges that the process currently has enabled become the authorized privileges of the subprocess. This means that even though a user may have OPER as an authorized privilege, unless the privilege is enabled at the time of the SPAWN, the subprocess will not be able to turn it on. This can be frustrating at the DCL level when you spawn a subprocess to do something and find that you can’t get enough privileges because you didn’t previously have them enabled. However, another implication of a subprocess receiving the enabled privileges can become a major security issue.
When a process executes a program that is installed with privileges, the IMAGPRIV mask is OR’ed with the process’s PRIVMSK mask. The mask of all currently enabled privileges consists of both the user’s privileges and the program’s privileges. The problem arises when home-grown software that must be installed with privileges allows users to spawn subprocesses to get to DCL. Since the currently enabled privileges become the subprocess’s authorized privileges, the spawning user may gain all privileges owned by the program! Program 1 demonstrates a method that should be used by all programs that may be installed and that call LIB$SPAWN.
The very first thing the program does is call $GETJPI to get the PRIVMSK and PROCPRIV masks for the current process. It then performs a complemented AND (using the BICL instruction) to clear all of the PROCPRIV privileges from PRIVMSK. If the program is installed, the resulting mask consists of all privileges enabled because of the program (any program privileges the user already had enabled are excluded from the mask). Once it has gotten the image privileges, the program calls $SETPRV to turn off those privileges and then calls LIB$SPAWN to spawn the subprocess. The resulting subprocess is prevented from enabling those privileges owned by the program. When the subprocess terminates, the program then re-enables all of the privileges turned off before the spawn.
The VMS privilege scheme provides a very flexible mechanism for controlling tasks that require privilege. Unlike other operating systems that have all-or-nothing privileged accounts and programs, the multitude of privileges under VMS helps managers and security officers keep privileged user actions to an easily manageable subset of all available tasks. Because some privileges are overloaded while the purposes of others seem obscure, the VMS privilege scheme is far from perfect. However, if care is taken, it can more effective than most other methods in use today!
Biographical Information:
Hunter Goatley, a graduate in Computer Science from Western Kentucky University, is currently working as a programmer/analyst for Clyde Digital Systems, Orem, Utah.
.TITLE SPAWN - Spawn a subprocess with proper privileges .IDENT /01-001/ ;++ ; ; Author: Hunter Goatley ; Clyde Digital Systems ; 371 East 800 South ; Orem, Utah 84058 ; 801-224-5306 ; ; Date: July 10, 1988 ; ; Functional description: ; ; This program is provided to demonstrate a method for ensuring ; that a subprocess does not gain privileges that an installed ; program owns. ; ; Modified by: ; ; 01-001 Hunter Goatley 10-JUL-1988 22:01 ; Original version. ; ;-- $JPIDEF ; Include $GETJPI symbols ; ; Read-only data ; .PSECT _SPAWN_DATA_R,NOEXE,NOWRT,LONG,PIC,SHR ; ; An argument list for the call to $GETJPIW. Following the list is an ; item list that tells $GETJPI to return the current and process-permanent ; privileges to locations PROCPRIV and CURPRIV. ; JPIARGS: $GETJPIW - ; $GETJPIW argument list ITMLST=10$ ; ... 10$: .WORD 8 ; Length is 8 bytes .WORD JPI$_CURPRIV ; Item code for CURPRIV .ADDRESS CURPRIV ; Address to receive mask .LONG 0 ; Don't need return length .WORD 8 ; Length is 8 bytes .WORD JPI$_PROCPRIV ; Item code for PROCPRIV .ADDRESS PROCPRIV ; Address to receive mask .LONG 0 ; Don't need length .LONG JPI$C_LISTEND ; End of item list ; ; Read/Write data ; .PSECT _SPAWN_DATA_RW,NOEXE,WRT,LONG,PIC PROCPRIV: .QUAD 0 ; Process-permanent privileges CURPRIV: .QUAD 0 ; Currently enabled privileges .PSECT _SPAWN_CODE,EXE,NOWRT,LONG,PIC,SHR .ENTRY SPAWN,^M<> ; ; Get the currently enabled privilege mask and the process-permanent privilege ; mask for this process. ; $GETJPIW_G - ; Get the CUR and PROC privilege JPIARGS ; ... masks for this process ; ; PROCPRIV points to the process-permanent privileges. CURPRIV points to ; the privileges currently enabled (including privileges belonging to the ; installed program). ; ; We want to disable all image privileges that are currently enabled. ; This is accomplished by performing a complemented AND of the two masks. ; The resulting mask in CURPRIV is the mask of all privileges that the ; program is installed with that do not really belong to us. ; ; BICL2 PROCPRIV,CURPRIV ; Clear PROC privileges from BICL2 PROCPRIV+4,CURPRIV+4 ; ... CUR privileges ; ; Now call $SETPRV to disable the image privileges. ; $SETPRV_S - ; Turn off image privileges ENBFLG=#0, - ; ... PRVADR=CURPRIV ; ... ; CALLS #0,G^LIB$SPAWN ; Spawn a subprocess ; PUSHL R0 ; Save status for a second $SETPRV_S - ; Turn the image privileges back ENBFLG=#1, - ; ... on PRVADR=CURPRIV ; ... POPL R0 ; Restore saved status RET ; Return to caller .END SPAWN