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


In the last two articles, we described the file heap and support classes. In this article we will describe the structures that are used in the sysUAF.dat file, which contains user information. First, let's look at some constant definitions which are used to define user flags and other user-related options. We'll start with the user authorization flags:

// UAF Flags...
const UAF_Audit = 1 ;
const UAF_AutoLogin = 2 ;
const UAF_Captive = 4 ;
const UAF_DefShell = 8 ;
const UAF_DisCtlY = 16 ; // Disable ^Y
const UAF_DisImage = 32 ; // Disable RUN
const UAF_Disreconnect = 64 ;
const UAF_DisReport = 128 ;
const UAF_Disabled = 256 ;
const UAF_DisWelcome = 512 ;
const UAF_DisAuth = 1024 ; // Cannot change password
const UAF_Restricted = 2048 ;
const UAF_Accounting = 2096 ;

The following describes these flags:
  • UAF_Audit: If set, security auditing is enabled for the user.
  • UAF_AutoLogin: If set, the user is restricted to the automatic login mechanism.
  • UAF_Captive: If set, the user is prevented from changing any defaults at login, and cannot leave the LGICMD command procedure specified for the user. Further, Ctrl/Y interrupts are initially disabled.
  • UAF_DefShell: If set, the user is restricted to UCL, the default UOS shell.
  • UAF_DisCtlY: If set, Ctrl/Y interrupts are initially turned off.
  • UAF_DisImage: If set, the user is prevented from executing the RUN command and any foreign commands.
  • UAF_Disreconnect: If set, the user is disabled from automatic reconnection when an interactive session is interrupted.
  • UAF_DisReport: If set, the login CUSP will not display the last login time, login failures, and other security reports.
  • UAF_Disabled: If set, the account is disabled and logins are not allowed.
  • UAF_DisWelcome: If set, the system welcome message is not displayed by the login CUSP.
  • UAF_DisAuth: If set, the user is not required to provide authentication. This is intended for use in conjunction with UAF_Captive.
  • UAF_Restricted: If set, the user is prevented from changing any defaults at login (such as specifying /LGICMD). Ctrl/Y is also initially disabled. Typically this is used to restrict a user to a specific application.
  • UAF_Accounting: If set, user accounting information is written to accounting.dat file.
Most of these flags will be discussed in more detail in future articles, as situations come up.

// Access types...
const UAT_Batch = 1 ;
const UAT_Interactive = 2 ;
const UAT_Network = 3 ;
const UAT_Remote = 4 ;

The access types indicate the means by which the user is logged in:
  • UAT_Batch: Batch logins.
  • UAT_Interactive: Interactive logins via physical connections (such as the monitor/keyboard on a PC) or via network (such as telnet).
  • UAT_Network: These are implicit logins that are used to authenticate the user when trying to access system resources (such as files) via a network connection.
  • UAT_Remote: Logins through another UOS system serving as a gateway (via the SET HOST command, for instance).
UAT_Interactive is the access type that we will discuss in the near future. We will discuss the remaining access types in detail in future articles.

// Authentication Methods...
const UAM_Password = 1 ;
const UAM_Auth = 2 ;

// Authentication method flags...
const UAMF_Generate = 1 ; // Generate password

The authentication methods, and flags, are used to define the way in which the user is authenticated (usually by password, indicated by UAM_Password):
  • UAM_Password: Authentication requires a password.
  • UAM_Auth: Custom authentication method.
  • UAMF_Generate: Automatically generate a password when the password is expired.

In order to prevent one user from monopolizing system resources on a multi-user time-shared machine, a set of quotas is defined for each user. Exceeding any of these quotas will generally result in the process aborting. Since it is not valid to have a quota of 0, we use a value of 0 to indicate an unlimited use of that resource. Typically, a personal computer would have user quotes set to 0 (unlimited), whereas a server would have them set such that an errant, or malicious, program cannot consume enough resources to interfere with the operation of other programs. Quotas also cannot be less than zero, so they are each represented by unsigned 32-bit integers. The TUAF_Quotas structure contains a complete set of quota information, with the exception of storage quotas. We will discuss storage quotas later, but the reason they are not included with the other quotas is that they are stored by device, and there can be an arbitrary number of stores - each with a different quota. The quotas are stored in the following structure:

type TUAF_Quotas = packed record
                       ASTLM : cardinal ; // Async I/Os
                       BIOLM : cardinal ; // Buffered I/Os
                       BYTLM : cardinal ; // Buffered I/O bytes
                       CPUTIM : cardinal ; // CPU seconds per session
                       DIOLM : cardinal ; // Raw I/Os
                       ENQLM : cardinal ; // Lock queue
                       FILLM : cardinal ; // Open files
                       MAXACCTJOBS : cardinal ; // Max batch jobs
                       MAXJOBS : cardinal ; // Max jobs
                       PGFLQUOTA : cardinal ; // Max pages in page file
                       TQELM : cardinal ;
                       WSDEFAULT : cardinal ; // Working Set default limit
                       WSEXTENT : cardinal ; // Max Working Set size
                       PRCLM : cardinal ; // Subprocess limit
                       THREADLM : cardinal ; // Thread limit
                       ETIME : cardinal ; // Elapsed connect time
                   end ;

  • ASTLM: Maximum number of simultaneous asynchronus I/O operations. When the quota is reached, asynchronous I/O requests are treated as synchronous I/Os. A misbehaving program could generate so many I/Os without blocking that it would effectively lock other processes out of I/O. The quota prevents that from happening.
  • BIOLM: Maximum number of I/Os that are buffered in system memory. I/Os can be written directly to user memory buffers, or to system I/O buffers and then copied to user buffers. The advantage of a buffered I/O is that the user memory doesn't have to be locked in place while waiting for the I/O operation to complete. The disadvantage is that more system memory is consumed.
  • BYTLM: Maximum number of I/O bytes per session.
  • CPUTIM: Maximum CPU time per session.
  • DIOLM: Maximum number of simultaneous direct I/O requests. These are I/O requests that have user memory buffers waiting for the I/O completion. Reaching this limit will block the program until one or more direct I/Os complete.
  • ENQLM: Maximum number of locks that a process can simultaneously hold.
  • FILLM: Maximum simultaneous open files.
  • MAXACCTJOBS: Maximum number of non-network processes that can be active simultaneously.
  • MAXJOBS: Maximum total number of processes (interactive, batch, detached, and network) that can be active simultaneously. The first four network jobs are not counted.
  • PGFLQUOTA: Maximum number of pages in the page file that the process is allowed to use.
  • TQELM: Maximum simultaneous timed queue events (timers) that a process can have active. Timers have system overhead, so limiting the number per process can reduce overall system overhead on multi-user systems.
  • WSDEFAULT: The number of memory pages that a process can use. Under some circumstances, WSEXTENT is used to limit process memory.
  • WSEXTENT: Maximum number of memory pages that a process can use.
  • PRCLM: Maximum number of simultaneous processes that a user can have running.
  • THREADLM: Maximum simultaneous threads per process.
  • ETIME: Amount of elapsed (connect) time per session.
We will discuss these quotas as relevant situations come up in future articles.

Now we examine the user record that is stored in the sysUAF.DAT file for each user.

type Ptr = int64 ;
     TList_Ptr = Ptr ;
     TString_Ptr = Ptr ;
     TStringList_Ptr = Ptr ;
     TTimeStamp = Ptr ;

     TUAF_User = packed record
                     Name : TString_Ptr ;
                     Flags : longint ; // User account flags (see UAF_*)
                     Authentication : TList_Ptr ; // Authentication schemes - list of pointers to TUAF_Authentication records
                     Access : TList_Ptr ; // Access list (see TUAF_Access record)
                     Shell : TString_Ptr ; // Default shell
                     LGICMD : TString_Ptr ; // Login command file name
                     Home : TString_Ptr ; // Default home folder
                     Privileges : int64 ; // Starting (default) privileges
                     Auth_Privileges : int64 ; // Authorized (allowed) privileges
                     Expiration : TTimeStamp ; // Account expiration
                     Owner : longint ; // User to charge accounting to (0=this user)
                     Priority : longint ; // Default priority
                     Quotas : TUAF_Quotas ; // Quotas
                     Last_Interactive_Login : TTimeStamp ;
                     Last_Non_Interactive_Login : TTimeStamp ;
                     Last_Login_Failure : TTimeStamp ;
                     Login_Failures : longint ; // Number of login failures since last successful login
                 end ;

First we define several types which are aliases of int64. These are used to document exactly what the various pointers in the TUAF_User record are. The structure's comments are self-explanatory, but we'll briefly cover them here.
  • Name: Pointer to the user name string header. Note that this is case-sensitive and matches the name used when the user is created. A normalized (lowercase) version is stored in a separate string list.
  • Flags: A combination of UAF_* flags (a bitmask), as described above.
  • Authentication: A list of pointers to authentication records, discussed below.
  • Access: A list of access records, discussed below.
  • Shell: The detault (starting) shell for the user. If this pointer is null or the string is empty, the system default shell is used as the default.li>
  • LGICMD: The login command filename. If null, or an empty string, this will default to a system login command file. Typically this is used for captive accounts.
  • Home: The home directory for the user. If null, or an empty string, the home directory for the user will default to "_SYS:\Users\x" where "x" is the user's name. For instance, "_SYS:\Users\Fred"
  • Privileges: The privileges that the process starts with after login.
  • Auth_Privileges: The authorized privileges for the user.
  • Expiration: When the account expires. Expired accounts cannot be logged into.
  • Owner: Normally 0, indicating that accounting information for this account is charged to this account. Otherwise, it is the UIC of the user whose account is to be charged.
  • Priority: Default process priority. If 0, the system default is used.
  • Quotas: This is the quota structure discussed above, containing the user's quotas.
  • Last_Interactive_Login: Timestamp of the last interactive login for this user.
  • Last_Non_Interactive_Login: Timestamp of the last non-interative login for this user.
  • Last_Login_Failure: Timestampe of the last failed login attempt.
  • Login_Failures: Number of login failures since the last successful login.

Constraining user access
By default, authorized users are allowed to log in at any time. However, on time-sharing systems it may be desirable to limit some users to certain hours of access. On personal computers, it might be desired by a parent to limit the times that a child is allowed to use a computer. In either case, each user can have a list of access records, which indicate the time periods that the user is allowed to log in. If there are no access records, there are no restrictions on the days and times that the user can log in. If there are access records, each one is checked at log-in time to see if the the current time matches one of the access records. If a match is found, the login is allowed. If not, login is denied. Here is the definition of an access record:

TUAF_Access = packed record
                  Typ : word ; // Access type (see UAT_*)
                  DOW : word ; // Day of week (-1 = any/all)
                  Starting : word ; // Starting minute of day
                  Ending : word ; // Ending minute of day
              end ;

Each record indicates a range (start and end) of a time period when access is allowed. These times are in minutes since midnight. Further, the record indicates which day of the week (or hex FFFF for all days). Finally, the access type indicates the type of login to which the access record applies. This allows for a very fine granularity of access control.

The SysUAF.Dat authentication record defines a means of authentication:

     TUAF_Authentication = packed record
                          Typ : longint ; // Authentication Method (see UAM_*)
                          Expiration : int64 ;
                          Access_Type : longint ; // Access type this record applies to (see UAT_*)
                          Auth : TString_Ptr ; // Password or authentication client name
                          Flags : longint ; // User athentication flags (see UAMF_*)
                          Description : TString_Ptr ; // Optional authentication description

                          // Password lifetime...
                          Lifetime : int64 ;
                          Last_Change : int64 ;
                      end ;

A user may have one or multiple Authentication methods required. The oldest, and most common, form of authentication is the text password. But more secure systems might require a DSA key or an iris scan. Even more secure systems might require a multiple authentication methods (for instance, a different password from two people, plus some biometric). If the UAF_Auto_Login flag is set, then no authentication methods are used. Otherwise, there must be at least one authentication record. If there are multiple records, each authentication method is processed in turn until all authentication is complete. If any of the authentications fail, the user is not logged in. This is an important security concept: If you think of security as a fence around the item being protected, then a single locked gate requires that someone has a key to get in. But if there are multiple gates in the fence, someone need only break through one of them to get in. The more gates in the fence, the less secure the perimeter is, because there are many possible keys - the chance that someone has the right key for one of the gates increases as the number of gates increases. But, if we have several concentric fences around the protected object, and each has a locked gate, then someone has to get through all of the gates. Each authentication method for a user adds another concentric fence around the user. The following diagram illustrates the difference between these two approaches:

In the example on the left, getting access requires getting through either gate. In the example on the right, getting access requires getting through both gates.

Let's look at the members of the authentication structure in more detail:

  • Typ: Authentication method. This is one of the UAM_* values.
  • Expiration: If 0, the password doesn't expire. Otherwise, this is the timestamp of when the password expires.
  • Access_Type: This indicates the access type (UAT_* flags) that this authentication method applies to. Different types of access can have different authentication methods.
  • Auth: If this is a password authentication method, this is the password value. Note: this is a hashed value rather than the actual password text. If this is an external authentication method, then this is the name of the CUSP that is called to do the authentication. If Description is non-null, the user is prompted for a response before the CUSP is called. If Description is null, prompting is left up to the CUSP.
  • Flags: This indicates flags for this authentication method. See UAMF_* values.
  • Description: If null, a default value is displayed to the user for authentication. For instance, for a password authentication method, the user would be prompted by the word "Password". Otherwise, this is the text that the user is prompted with.
  • Lifetime: This is ignored for non-password records. If 0, the password doesn't autoexpire. Otherwise, this is the length of time (in ns) until a new password expires. When a password is changed, the Expiration date is set to the current date/time plus this value.
  • Last_Change: Ignored except for password authentication records. Otherwise, this is the timestamp of the last password change.

In the next article, we will discuss the TUser class, which provides an interface to these sysUAF.DAT structures, and how the class is used by the USC to log a user in.