1 Introduction
2 Ground Rules

Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class

The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up

The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup

Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser

Terminal I/O
45 Shells and UCL
46 UOS API, the Application Side
47 UOS API, the Executive Side
48 I/O Devices
49 Streams
50 Terminal Output Filters
51 The TTerminal Class
52 Handles
53 Putting it All Together
54 Getting Terminal Input
55 QIO
56 Cooking Terminal Input
57 Putting it all together, part 2
58 Quotas and I/O

59 UCL Basics
60 Symbol Substitution
61 UCL Command execution
62 UCL Command execution, part 2
63 UCL Command Abbreviation


Download sources
Download binaries

Introduction to Users and Security

Computer security is important. Security threats can come two sources: malicious intent and mistakes. Both are handled the same way. After all, if you lose an important file, it matters not if it was lost because of a hacker or because you made an honest mistake.
To protect files, we need a means of authenticating access to computer resources. When more than one person uses the computer, it may be desirable to distinguish between them when it comes to which resources can be accessed, changed, or deleted.
Therefore, most operating systems control access via a user name and a password. The term "user" may be a bit of a misnomer because more than one person might use the same "user" name. This is up to the system administrator or course, although it is a good idea to make sure that each person accessing the system have a unique user name (one exception would be the case of "guest" users, which we will discuss later). So, henceforth, we will make a distinction between people and users, Sometimes a user is referred to as an "account" or "user account" because we keep track of usage by user, which can be used for accounting purporses. Thus, a UOS "user" is a persistent named set of data that indicates some level of access to the operating system and its resources (such as files).

We need more than a user name, however. We need a means of authenticating a user when they want to access the system. "Authentication" is simply some means of verifying that a person is the user that they claim to be. The simplest method of authentication (and oldest) is a password or pass phrase. In theory, only the actual person and the computer know the password and, therefore, if the user provides the correct password, that proves that they are authorized to access the computer as that user. In practice, passwords are often shared, written down (and subsequently stolen), or are easily guessed. So there are other means of authentication. UOS supports other authentication methods, which we will discuss in the future. For now, we will limit ourselves to passwords.

Some older operating system provided no security - such as CP/M, MSDOS, the original UNIX (from which Linux derives), or the original version of Windows. In the case of UNIX and Windows, security was added as an afterthought, which created some deciedly unelegant implementations - and no small amount of pain for users. Adding security after the fact is a good way to create unsecure systems that are easy to break into. UOS involves security from the start to make it secure. Security "holes" (means of bypassing security protections) can be created by actions of the system administrator or by software. We cannot prevent an administrator from creating holes in security, but we can offer guidance, secure defaults, and otherwise make it difficult for him to create such holes - especially important on a personal system where the owner isn't likely knowledgeable about security issues.
VMS is widely considered to be a secure operating system, which is another reason why we will be modeling UOS after it. We will depart from the VMS security model only with careful consideration, if at all. Like VMS, UOS uses the Security Reference Model which simply defines security in terms of 1) the user requesting access to a resource, 2) that resource, and 3) a database of rules about who can access which resources. Each time a user attempts access to a resource, the database is checked to see if that form of access is allowed for that user and resource. The database can be conceptualized as a simple table, but it is more complicated in implementation.

So we will be discussing security in most of the articles to follow, as it applies to the topic at hand. But in the next few articles we will lay the foundation for all following user and security issues.

User names and IDs
Let's review the file header for the UOS file system, from article 8:

     TUOS_File_Header = packed record
                                Name : int64 ;
                                Size : int64 ;
                                EOF : int64 ;
                                Uncompressed_Size : int64 ;
                                Clustersize : int64 ;
                                Record_Size : int64 ;

                                Creation : int64 ;
                                Last_Modified : int64 ;
                                Last_Backup : int64 ;
                                Last_Access : int64 ;
                                Expiration : int64 ;

                                Flags : int64 ;
                                Creator : int64 ;
                                Owner : int64 ;
                                ACL : int64 ;
                                Extension : TStore_Address64 ;
                                Version_Limit : longint ;
                                Extended_Flags : longint ;

                                Streams : array[ 0..5 ] of TData_Stream ;

                                Data_Stream : int64 ;
                                reserved : int64 ;
                                File_System : int64 ;
                        end ;

Flags, Creator, Owner, and ACL are all used for security. First, let's consider the Owner value. This is the User ID for the owner of the file. We only have 8 bytes for storing a user with the file, and a username can be longer than that. So, UOS associates an integer value with each user name. In fact, UOS operates solely based upon the user ID. The name is simply to make it easier on the humans that use the system. We will discuss how that association is done later. However, to prevent confusion, all user IDs and user names are unique. User names can consist of alphanumeric characters, dollar signs ($), and underscores (_). There is, essentially, no limit on their length, but they must be at least one character in length.
When a user creates a file, the file is automatically assigned to his user ID code (UIC). The owner of the file, by default, can do just about anything he wants with that file - read, write, change, delete, shrink, extend, etc. However, some of these actions may require some additional security checks. For instance, extending the size of a file may not be permitted if the user has exceeded his quota of space on a given store. We will discuss these issues as they come up.
The lowest UICs (usually 1-7, but that can be customized) are system UICs and are given special consideration. For instance, a system UIC is allowed to log in on the system console even if logins are disabled. We will discuss other special circumstances as they come up. A UIC of 0 indicates no ownership, and is never seen on a file. Pre-defined user accounts:
  • 1: Startup. This account can never be logged into. On system startup, the process is forced to this user.
  • 2: System. This is the default system administrator account.
  • 3-7: reserved for future use

ACL is a pointer to an optional allocation chain consisting for Access Control List data (discussed later in this article).

File protection
Access to a file is categorized four ways: read, write, delete, and execute (abbreviated R, W, D, and E). A user with read access to a file can read the contents of that file. Write access allows the file's data to be modified. Delete access allows the file to be deleted. Execute access allows the user to execute a file (if it is an execuatble image). The reason that read and execute access are separate is that although execution requires the file to be read into memory, this is done without the user being able to see the contents. If the user can read the file, he might be able to search the executable for an included password, or some other confidential embedded information. Also note that just because a file has the Execute protection doesn't mean that it is actually executable. You could set the Execute protection on a text file, but it won't "run". Allowing execution simply means that the user can request execution of the file. Whether that request results in an error or does something useful is dependent upon whether it is a executable image. In other words, protections indicate what is allowed on a file - they don't indicate what the file is. Conversely, even if the file is an executable image any attempt to execute it will fail if the Execute protection isn't set.
Users are divided into four categories for determining who has which kind of access. These categories are as follows:

  • System: Members of this category include any of the following: users with system UICs (see above), users with SYSPRV privilege, users with GRPPRV privilege whose group matches the owner's group, and users whose UIC matches the UIC of the store on which the file exists.
  • Owner: The user whose UIC matches the UIC of the file.
  • Group: All users with a group matching the file owner's group(s). We will discuss groups in a future article.
  • World: All users, including the above three categories.

The categories are abbreviated as S, O, G, and W. A full protection specification is a comma-delimited list where each item is a category followed by a colon and the access types. For instance "S:RWE,O:RW,G:R,W:D" would indicate that system has read, write, and execute, while the owner has read and write, group has read-only, and world has delete access. As mentioned above, upon creation, the owner is given RWED access to the file.
The following constants have been added to define protection codes in the file header flags:

      FAF_PROTECTION_WORLD_READ = $10000000 ;
      FAF_PROTECTION_WORLD_WRITE = $20000000 ;

Access Control Lists
File protections are a good means of determining who has access to files, and very similar means are provided by all modern operating systems. However, it isn't as flexible as we'd like. To provide finer resolution to who is allowed/denied access, we use a mechanism called Access Control Lists (or ACLs). ACLs are also used in Windows (Microsoft used the ACL concept from VMS). An ACL consists of a list of Access Control Entries (ACEs), each of which either allows or denies access for a user and a specific type of access. We will discuss ACLs further in a future article. The basic protection mechanism works this way: the user and the requested access is compared with the file protection. This will result in either an access granted or access denied state. Then the ACL is processed to see if there is a rule (ACE) that changes that result. Note that any given ACL can have entries that both allow and deny access for various users. This can result in some complicated rules that allow access to a file even when not intended. To avoid these kinds of problems, it is recommended that either: 1) the file protections be set very restrictive and the ACL would only include entries that grant access, or 2) the file protections be set unrestrictive and the ACL would only include entries that deny access. The first option is generally the best way to avoid problems.

It isn't only files that have access permissions. All system resources accessible outside of the kernel have access permissions as well. For instance, the symbol tables that we talked about in article 35 have protection codes (and possibly ACLs). By default, all users have read access to the symbol tables, but most users have write access only to their job and process tables. As we continue these articles, we will come across other resources that have protection as well.

There are a class of UOS features that are not protected with the above security protections. For instance, the ability to add new users. Users are granted access to these features via privileges. Privileges are flags that indicate whether or not a user has a given ability to affect UOS. Most users have no privileges for security purposes - that way if the user runs a virus, it cannot harm the rest of the system. There are four privileges that affect the operation of protections:

  • BYPASS - User has RWED access to all files, bypassing file protections
  • READALL - User has read access to all files, bypassing file protections
  • SYSPRV - User accesses all files via the file's system protection
  • GRPPRV - User accesses all files via the file's group protection
The remaining privileges are briefly described below (we will cover them in more detail in the future):
  • ACNT - Run processes with accounting disabled.
  • ALLSPOOL - Allows user to allocate spooled devices.
  • ALTPRI - Alter priorities.
  • AUDIT - Allow audit records to be written.
  • BUGCHK - Allow messages to error logger.
  • CMEXEC - Allow calls to Change Mode to Supervisor system service.
  • CMKRNL - Allow call to Change Mode to Kernel/Executive system service.
  • DIAGNOSE - Allow user to run diagnostics and intercept error log messages.
  • EXQUOTA - Allows user to exceed usage quotas.
  • GROUP - Allows user to affect other processes belonging to common group.
  • GRPNAM - Allows user to use /GROUP on mount and dismount operations.
  • IMPERSONATE - Allows detached processes to be created with a different UIC.
  • LOG_IO - Allow certain device control functions.
  • MOUNT - Allows user to mount volumes.
  • NETMBX - Allow network control operations.
  • OPER - Allows use of OPCOM.
  • PFNMAP - Allows unrestricted access to physical memory.
  • PHY_IO - Allows physical I/O operations.
  • PRMCEB - Allows creation/deletion of permanent common event flag clusters.
  • PRMGBL - Allows creation/deletion of permanent global sections.
  • PRMMBX - Allows creation/deletion of permanent mailboxes.
  • PSWAPM - Allows control of swapping operations.
  • SECURITY - Allow user to perform security-related functions.
  • SETPRV - Allows user to create processes that have privileges greater than the user.
  • SHARE - Allows user to open assigned devices or to assign nonshared devices.
  • SHMEM - Allows user to create global sections and mailboxes in memory shared by multiple processors.
  • SYSGBL - Allows user to create/delete system global sections.
  • SYSLCK - Allows user to process locks.
  • SYSNAM - Allows user to bypass access controls on system symbol tables.
  • TMPMBX - Allows user to create temporary mailbox.
  • VOLPRO - Allows user to override protections on volumes.
  • WORLD - Allows user to control any/all other processes.

Obviously, if any user can grant themselves privileges, the system isn't secure. The most important rule in regard to how UOS handles privileges is that a user cannot grant privileges to any object that are greater than the privileges he has. For instance, a program can create sub-processes. These new processes can be granted any privileges that the user running the program has, but they cannot be granted privileges that the creator process doesn't have. The one exception to the rule is that the SETPRV privileges allows a process to grant additional privileges to itself or another process. Obviously, this is a dangerous privilege and is only granted to a couple trusted CUSPs. It is possible, however, for a user to grant privileges to a exectuable such that any user that runs the executable will have those privileges while the program is running, even if the user running the program doesn't have those privileges. Certain CUSPs have such privileges in order to perform functions on behalf of a user without otherwise sufficient privileges.
In order for this privilege scheme to work, a running process has four sets of privilege:

  • Granted privileges: These are the privileges granted to the user account.
  • Current privileges: This is a subset of the granted privileges that indicate which privileges are currently in effect.
  • Program privileges: These are the privileges granted to the currently running program. If a program is not running, these flags are all cleared.
  • Effective privileges: this is the same as Current privileges merged with any program privileges.
Whenever the user makes a request to UOS, the Effective privileges are checked against - regardless of the Granted privileges. In general, you don't want to operate with all possible granted privileges active in order to avoid costly mistakes. Thus, you can drop any privileges from your current privileges or explictly elevate your current privileges to include any/all of your granted privileges. Likewise, programs should drop any privileges they don't need and only elevate those that are needed for a specific operation. A program can elevate the effective privileges to any combination of the Current privileges and Program privileges.
Let's consider an example of how this all works. Assume that user Fred is responsible for making weekly backups of all the files that have changed that week. He is granted READALL privilege so he can make backup copies of any changed file on the system. However, he only wants this privilege while doing the backup. Thus, he runs without that privilege until he does the backup. At that point, he elevates his current privileges to include READALL and then executes the backup utility. The backup program has BYPASS privilege, but drops this from the process' effective privileges so that user can only read files that he has access to (normally a user can only backup files that he has read access to - the backup program doesn't know that Fred has READALL privilege). However, once the file has been backed up, the backup program elevates the effective privileges to include BYPASS so that it can set the Last Backup field in the file header. Then it lowers itself back to the process' current privileges. If the program left BYPASS on all the time, then it would be granted access to all files which would allow any user to use the backup program to make copies of files that he wouldn't otherwise be privy to. Of course, if the user's current privileges included BYPASS, then the program would run with BYPASS all the time, but that would be okay since the user can already bypass file protections. When the program stops executing, the effective privileges are reset to the Current privileges and the Program privileges value is cleared, thus resetting the effective privileges to the current process privileges.
Note that VMS uses a different mechanism than UOS for assigning privileges to executable files. In UOS, this is a special kind of entry in the ACL. We will, more often than not, follow the VMS implementation of various features (although not the precise data structure layout), but sometimes we will diverge since we are more concerned with UOS being functionally equivalent than being a clone.

So, from where do privileges originally derive? If nothing can elevate privileges beyond what it already has, how can any user be granted any privileges whatsoever? The Startup user has all privileges automatically granted to it when UOS is installed. The system startup is always run under the auspices of the Startup user. The first time system startup runs, the process creates a System account that also has all privileges. It also creates one or more user accounts that are granted some subset of privileges (or none at all).
The privileges are stored as a 64-bit mask in the SYSUAF (System User Authorization File) file. The file is \uos\sysuaf.dat. On VMS, sysuaf is an RMS (Record Management System) file. RMS is a service that provides various file I/O, such as a B-Tree database table. UOS will support RMS as well, but we are not building it into the UOS executive in order to keep the executive as small as possible. Because the executive needs to access sysuaf to maintain security, we will use a different (simpler) mechanism to access the data. VMS also stores security information in the rightslist.dat file, but UOS combines everything related to users in the sysuaf.dat file.

To summarize, all security ultimately depends upon the protections and privileges granted to users. Since all UOS activities (after Kernel startup) are ultimately the result of user actions, we consolidate both user and security-related code in the USC (User and Security Component) - user accounts and security are essentially the same thing. Much of the code that we will discuss in the next few articles will be in, or related to, the USC.

In the next article, we will discuss the File Heap, which is how we will implement the user authorization file.