Chapter 8
Access Security

 8.1 Overview
 8.2 Quick Start
 8.3 User’s Guide
  8.3.1 Features
  8.3.2 Limitations
  8.3.3 Definitions
  8.3.4 Access Security Configuration File
  8.3.5 ascheck - Check Syntax of Access Configuration File
  8.3.6 IOC Access Security Initialization
  8.3.7 Database Configuration
  8.3.8 Example:
 8.4 Design Summary
  8.4.1 Summary of Functional Requirements
  8.4.2 Additional Requirements
  8.4.3 Design Overview
  8.4.4 Comments
  8.4.5 Performance and Memory Requirements
 8.5 Access Security Application Programmer’s Interface
  8.5.1 Introduction
  8.5.2 Definitions
  8.5.3 Initialization
  8.5.4 Group manipulation
  8.5.5 Client Manipulation
  8.5.6 Access Computation
  8.5.7 Diagnostics
 8.6 Database Access Security
  8.6.1 Access Level definition
  8.6.2 Access Security Group definition
  8.6.3 Database Access Library
 8.7 Channel Access Security
  8.7.1 CA Server Interfaces to the Access Security System
  8.7.2 Client Interfaces
 8.8 Trapping Channel Access Writes
 8.9 Access Control: Implementation Overview
  8.9.1 Implementation Overview
  8.9.2 Locking
 8.10 Structures

8.1 Overview

This chapter describes access security, i.e. the system that limits access to IOC databases. It consists of the following sections:

Overview
This section
Quick start
A summary of the steps necessary to start access security.
User’s Guide
This explains what access security is and how to use it.
Design Summary
Functional Requirements and Design Overview.
Application Programmer’s Interface
Database Access Security
Access Security features for EPICS IOC databases.
Channel Access Security
Access Security features in Channel Access
Trapping Channel Access Writes
This allows trapping of all writes from external channel access clients.
Implementation Overview

The requirements for access security were generated at ANL/APS in 1992. The requirements document is:

EPICS: Channel Access Security - Functional Requirements, Ned D. Arnold, 03/-9/92.

This document is available through the EPICS website.

8.2 Quick Start

In order to “turn on” access security for a particular IOC the following must be done:

The following rules decide if access security is turned on for an IOC:

After an IOC has been booted with access security enabled, the access security rules can be changed by issuing the asSetFilename, asSetSubstitutions, and asInit. The functions asInitialize, asInitFile, and asInitFP, which are described below, can also be used.

8.3 User’s Guide

8.3.1 Features

Access security protects IOC databases from unauthorized Channel Access Clients. Access security is based on the following:

Who
Userid of the channel access client.
Where
Hostid where the user is logged on. This is the host on which the channel access client exists. Thus no attempt is made to see if a user is local or is remotely logged on to the host.
What
Individual fields of records are protected. Each record has a field containing the Access Security Group (ASG) to which the record belongs. Each field has an access security level, either ASL0 or ASL1. The security level is defined in the record definition file. Thus the access security level for a field is the same for all record instances of a record type.
When
Access rules can contain input links and calculations similar to the calculation record.

8.3.2 Limitations

An IOC database can be accessed only via Channel Access or via the vxWorks or ioc shell. It is assumed that access to the local IOC console is protected via physical security, and that network access is protected via normal networking and physical security methods.

No attempt has been made to protect against the sophisticated saboteur. Network and physical security methods must be used to limit access to the subnet on which the iocs reside.

8.3.3 Definitions

This document uses the following terms:

ASL
Access Security Level (Called access level in Req Doc)
ASG
Access Security Group (Called PV Group in Req Doc)
UAG
User Access Group
HAG
Host Access Group

8.3.4 Access Security Configuration File

This section describes the format of a file containing definitions of the user access groups, host access groups, and access security groups. An IOC creates an access configuration database by reading an access configuration file (the extension .acf is recommended). Lets first give a simple example and then a complete description of the syntax.

8.3.4.1 Simple Example
UAG(uag) {user1,user2}
HAG(hag) {host1,host2}
ASG(DEFAULT) {
    RULE(1,READ)
    RULE(1,WRITE) {
        UAG(uag)
        HAG(hag)
    }
}

These rules provide read access to anyone located anywhere and write access to user1 and user2 if they are located at host1 or host2.

8.3.4.2 Syntax Definition

In the following description:

[ ] surrounds optional elements
| separates alternatives
... means that an arbitrary number of definitions may be given.
# introduces a comment line

The elements <name>, <user>, <host>, <pvname> and <calculation> can be given as quoted or unquoted strings. The rules for unquoted strings are the same as for database definitions.

UAG(<name>) [{ <user> [, <user> ...] }]
...
HAG(<name>) [{ <host> [, <host> ...] }]
...
ASG(<name>) [{
    [INP<index>(<pvname>)
    ...]
    RULE(<level>,NONE | READ | WRITE [, NOTRAPWRITE | TRAPWRITE]) {
        [UAG(<name> [,<name> ...])]
        [HAG(<name> [,<name> ...])]
        CALC(<calculation>)
    }
    ...
}]
...

8.3.4.3 Discussion

Each IOC record contains a field ASG, which specifies the name of the ASG to which the record belongs. If this field is null or specifies a group which is not defined in the access security file then the record is placed in group DEFAULT.

The access privilege for a channel access client is determined as follows:

  1. The ASG associated with the record is searched.
  2. Each RULE is checked for the following:

    1. The field’s level must be less than or equal to the level for this RULE.
    2. If UAG is defined, the user must belong to one of the specified UAGs. If UAG is not defined all users are accepted.
    3. If HAG is defined, the user’s host must belong to one one of the HAGs. If HAG is not defined all hosts are accepted.
    4. If CALC is specified, the calculation must yield the value 1, i.e. TRUE. If any of the INP fields associated with this calculation are in INVALID alarm severity the calculation is considered false. The actual test for TRUE is .99 < result < 1.01.
  3. The maximum access allowed by step 2 is the access chosen.

Multiple RULEs can be defined for a given ASG, even RULEs with identical levels and access permissions. The TRAPWRITE setting used for a client is determined by the first WRITE rule that passes the rule checks.

8.3.5 ascheck - Check Syntax of Access Configuration File

After creating or modifying an access configuration file it can be checked for syntax errors by issuing the command:

ascheck -S "xxx=yyy,..." < "filename"

This is a Unix command. It displays errors on stdout. If no errors are detected it prints nothing. Only syntax errors not logic errors are detected. Thus it is still possible to get your self in trouble. The flag -S means a set of macro substitutions may appear. This is just like the macro substitutions for dbLoadDatabase.

8.3.6 IOC Access Security Initialization

In order to have access security turned on during IOC initialization the following command must appear in the startup file before iocInit is called:

asSetFilename("/full/path/to/access/security/file.acf")

If this command is not used then access security will not be started by iocInit. If an error occurs when iocInit calls asInit than all access to the ioc is disabled, i.e. no channel access client will be able to access the ioc. Note that this command does not read the file itself, it just saves the argument string for use later on, nor does it save the current working directory, which is why the use of an absolute path-name for the file is recommended (a path name could be specified relative to the current directory at the time when iocInit is run, but this is not recommended if the IOC also loads the subroutine record support as a later reload of the file might happen after the current directory had been changed).

Access security also supports macro substitution just like dbLoadDatabase. The following command specifies the desired substitutions:

asSetSubstitutions("var1=sub1,var2=sub2,...")

This command must be issued before iocInit.

After an IOC is initialized the access security database can be changed. The preferred way is via the subroutine record described in the next section. It can also be changed by issuing the following command to the vxWorks shell:

asInit

It is also possible to reissue asSetFilename and/or asSetSubstitutions before asInit. If any error occurs during asInit the old access security configuration is maintained. It is NOT permissable to call asInit before iocInit is called.

Restarting access security after ioc initialization is an expensive operation and should not be used as a regular procedure.

8.3.7 Database Configuration

8.3.7.1 Access Security Group

Each database record has a field ASG which holds a character string. Any database configuration tool can be used to give a value to this field. If the ASG of a record is not defined or is not equal to a ASG in the configuration file then the record is placed in DEFAULT.

8.3.7.2 Subroutine Record Support

Two subroutines, which can be attached to a subroutine record, are available (provided with iocCore):

asSubInit
asSubProcess

NOTE: These subroutines are automatically registered thus do NOT put a registrar definition in your database definition file.

If a record is created that attaches to these routines, it can be used to force the IOC to load a new access configuration database. To change the access configuration:

  1. Modify the file specified by the last call to asSetFilename so that it contains the new configuration desired.
  2. Write a 1 to the subroutine record VAL field. Note that this can be done via channel access.

The following action is taken:

  1. When the value is found to be 1, asInit is called and the value set back to 0.
  2. The record is treated as an asynchronous record. Completion occurs when the new access configuration has been initialized or a time-out occurs. If initialization fails the record is placed into alarm with a severity determined by BRSV.

8.3.7.3 Record Type Description

Each field of each record type has an associated access security level of ASL0 or ASL1. See the chapter “Database Definition” for details.

8.3.8 Example:

Lets design a set of rules for a Linac. Assume the following:

  1. Anyone can have read access to all fields at anytime.
  2. Linac engineers, located in the injection control or control room, can have write access to most level 0 fields only if the Linac is not in operational mode.
  3. Operators, located in the injection control or control room, can have write access to most level 0 fields anytime.
  4. The operations supervisor, linac supervisor, and the application developers can have write access to all fields but must have some way of not changing something inadvertently.
  5. Most records use the above rules but a few (high voltage power supplies, etc.) are placed under tighter control. These will follow rules 1 and 4 but not 2 or 3.
  6. IOC channel access clients always have level 1 write privilege.

Most Linac IOC records will not have the ASG field defined and will thus be placed in ASG DEFAULT. The following records will have an ASG defined:

The following access configuration satisfies the above rules.

UAG(op) {op1,op2,superguy}
UAG(opSup) {superguy}
UAG(linac) {waw,nassiri,grelick,berg,fuja,gsm}
UAG(linacSup) {gsm}
UAG(appDev) {nda,kko}
HAG(icr) {silver,phebos,gaea}
HAG(cr) {mars,hera,gold}
HAG(ioc) {ioclic1,ioclic2,ioclid1,ioclid2,ioclid3,ioclid4,ioclid5}
ASG(DEFAULT) {
    INPA(LI:OPSTATE)
    INPB(LI:lev1permit)
    RULE(0,WRITE) {
        UAG(op)
        HAG(icr,cr)
        CALC("A=1")
    }
    RULE(0,WRITE) {
        UAG(op,linac,appdev)
        HAG(icr,cr)
        CALC("A=0")
    }
    RULE(1,WRITE) {
        UAG(opSup,linacSup,appdev)
        CALC("B=1")
    }
    RULE(1,READ)
    RULE(1,WRITE) {
        HAG(ioc)
    }
}
ASG(permit) {
    RULE(0,WRITE) {
        UAG(opSup,linacSup,appDev)
    }
    RULE(1,READ)
    RULE(1,WRITE) {
        HAG(ioc)
    }
}
ASG(critical) {
    INPB(LI:lev1permit)
    RULE(1,WRITE) {
        UAG(opSup,linacSup,appdev)
        CALC("B=1")
    }
    RULE(1,READ)
    RULE(1,WRITE) {
        HAG(ioc)
    }
}

8.4 Design Summary

8.4.1 Summary of Functional Requirements

A brief summary of the Functional Requirements is:

  1. Each field of each record type is assigned an access security level.
  2. Each record instance is assigned to a unique access security group.
  3. Each user is assigned to one or more user access groups.
  4. Each node is assigned to a host access group.
  5. For each access security group a set of access rules can be defined. Each rule specifies:

    1. Access security level
    2. READ or READ/WRITE access.
    3. An optional list of User Access Groups or * meaning anyone.
    4. An optional list of Host Access Groups or * meaning anywhere.
    5. Conditions based on values of process variables

8.4.2 Additional Requirements

8.4.2.1 Performance

Although the functional requirements doesn’t mention it, a fundamental goal is performance. The design provides almost no overhead during normal database access and moderate overhead for the following: channel access client/server connection, ioc initialization, a change in value of a process variable referenced by an access calculation, and dynamically changing a records access control group. Dynamically changing the user access groups, host access groups, or the rules, however, can be a time consuming operation. This is done, however, by a low priority IOC task and thus does not impact normal ioc operation.

8.4.2.2 Generic Implementation

Access security should be implemented as a stand alone system, i.e. it should not be imbedded tightly in database or channel access.

8.4.2.3 No Access Security within an IOC

Within an IOC no access security is invoked. This means that database links and local channel access clients calls are not subject to access control. Also test routines such as dbgf should not be subject to access control.

8.4.2.4 Defaults

It must be possible to easily define default access rules.

8.4.2.5 Access Security is Optional

When an IOC is initialized, access security is optional.

8.4.3 Design Overview

The implementation provides a library of routines for accessing the security system. This library has no knowledge of channel access or IOC databases, i.e. it is generic. Database access, which is responsible for protecting an IOC database, calls library routines to add each IOC record to one of the access control groups.

Lets briefly discuss the access security system and how database access and channel access interact with it.

8.4.3.1 Configuration File

User access groups, host access groups, and access security groups are configured via an ASCII file.

8.4.3.2 Access Security Library

The access security library consists of the following groups of routines: initialization, group manipulation, client manipulation, access computation, and diagnostic. The initialization routine reads a configuration file and creates a memory resident access control database. The group manipulation routines allow members to be added and removed from access groups. The client routines provide services for clients attached to members.

8.4.3.3 IOC Database Access Security

The interface between an IOC database and the access security system.

8.4.3.4 Channel Access Security

Whenever the Channel Access broadcast server receives a ca_search request and finds the process variable, it calls asAddClient. Whenever it disconnects it calls asRemoveClient. Whenever it issues a get or put to the database it must call asCheckGet or asCheckPut.

8.4.4 Comments

It is likely that the access rules will be defined such that many IOCs will attach to a common process variable. As a result the IOC containing the PV will have many CA clients.

What about password protection and encryption? I maintain that this is a problem to be solved in a level above the access security described in this document. This is the issue of protecting against the sophisticated saboteur.

8.4.5 Performance and Memory Requirements

Performance has not yet been measured but during the tests to measure memory usage no noticeable change in performance during ioc initialization or during Channel Access clients connection was noticed. Unless access privilege is violated the overhead during channel access gets and puts is only an extra comparison.

In order to measure memory usage, the following test was performed:

  1. A database consisting of 5000 soft analog records was created.
  2. A channel access client (caput) was created that performs ca_puts on each of the 5000 channels. Each time it begins a new set of puts the value increments by 1.
  3. A channel access client (caget) was created that has monitors on each of the 5000 channels.

The memory consumption was measured before iocInit, after iocInit, after caput connected to all channels, and after caget connected to all 5000 channels. This was done for APS release 3.11.5 (before access security) and the first version which included access security. The results were:

R3.11.5

After




Before iocInit

4,244,520

4,860,840

After iocInit

4,995,416

5,964,904

After caput

5,449,780

6,658,868

After caget

8,372,444

9,751,796

Before the database was loaded the memory used was 1,249,692 bytes. Thus most of the memory usage before iocInit resulted from storage for records. The increase since R3.11.5 results from added fields to dbCommon. Fields were added for access security, synchronous time support and for the new caching put support. The other increases in memory usage result from the control blocks needed to support access control. The entire design was based on maximum performance. This resulted in increased memory usage.

8.5 Access Security Application Programmer’s Interface

8.5.1 Introduction

File asLib.h describes the access security data structures and the last section of this chapter has a diagram describing the relationship between the structures. The structures are:

All structures except ASGMEMBER and ASGCLIENT are created by the access security library itself when it reads an access security file. An ASGMEMBER is created each time asAddMember is called by code that interfaces to the database. An ASGCLIENT is created each time asAddClient is called by a channel access server.

8.5.2 Definitions

The following are descriptions of arguments of routines described later.

typedef struct asgMember ASMEMBERPVT; 
typedef struct asgClient ASCLIENTPVT; 
typedef int (⋆ASINPUTFUNCPTR)(char buf,int max_size); 
typedef enum{ 
    asClientCOAR   /⋆Change of access rights⋆/ 
    /⋆For now this is all⋆/ 
} asClientStatus; 
typedef void (⋆ASCLIENTCALLBACK)(ASCLIENTPVT,asClientStatus);

8.5.3 Initialization

long asInitialize(ASINPUTFUNPTR inputFunction); 
long asInitFile(const char filename,const char substitutions); 
long asInitFP(FILE fp,const char substitutions);

These routines read an access definition file and perform all initialization necessary. The caller must provide a routine to provide input lines for asInitialize. asInitFile and asInitFP do their own input and also perform macro substitutions.

The initilization routines can be called multiple times. If an access system already exists the old definitions are removed and the new one initialized. Existing members are placed in the new ASGs.

8.5.4 Group manipulation

The routines are called by code that knows how to associate ASG names with the database. In the case of IOC databases, dbCommon has a field ASG. At IOC initialization a call is made to asAddMember for every record instance in the IOC database.

8.5.4.1 add Member
long asAddMember(ASMEMBERPVT ppvt, const char asgName);

This routine adds a new member to ASG asgName. The calling routine must provide storage for ASMEMBERPVT. Upon successful return ⋆ppvt will be equal to the address of storage used by the access control system. The access system keeps an orphan list for all asgNames not defined in the access configuration.

The caller must provide permanent storage for asgName.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.4.2 remove Member
long asRemoveMember(ASMEMBERPVT ppvt);

This routine removes a member from an access control group. If any clients are still present it returns an error status of S_asLib_clientExists without removing the member.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.4.3 get Member Pvt
void asGetMemberPvt(ASMEMBERPVT pvt);

For each member, the access system keeps a pointer that can be used by the caller. This routine returns the value of the pointer.

This routine returns NULL if access security is not active

8.5.4.4 put Member Pvt
long asPutMemberPvt(ASMEMBERPVT pvt,void userPvt);

This routine is used to set the pointer returned by asGetMemberPvt.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.4.5 change Group
long asChangeGroup(ASMEMBERPVT ppvt,const char newAsgName);

This routine changes the group for an existing member. The access rights of all clients of the member are recomputed.

The caller must provide permanent storage for newAsgName.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.5 Client Manipulation

This code is called by a channel access server.

8.5.5.1 add Client
long asAddClient(ASCLIENTPVT ppvt, ASMEMBERPVT pvt, int asl, 
                 const char user, char host);

This routine adds a client to an ASG member. The calling routine must provide storage for the ASCLIENTPVT pointer. ASMEMBERPVT is the value that was set by calling asAddMember. The database code and the server code must develop a convention that allows the server code to locate the ASMEMBERPVT. For IOC databases, ASMEMBERPVT is kept in dbCommon. asl is the access security level.

The caller must provide permanent storage for user and host. Note that user is “const char *” but host is just “char *”. The reason is the host names are converted to lower case.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.5.2 change Client
long asChangeClient(ASCLIENTPVT ppvt, int asl, 
                    const char user, char host);

This routine changes one or more of the values asl, user, and host for an existing client. Again the caller must provide permanent storage for user and host. It is permissible to use the same user and host used in the call to asAddClient with different values.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.5.3 remove Client
long asRemoveClient(ASCLIENTPVT pvt);

This call removes a client.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.5.4 get Client Pvt
void asGetClientPvt(ASCLIENTPVT pvt);

For each client, the access system keeps a pointer that can be used by the caller. This routine returns the value of the pointer.

This routine returns NULL if access security is not active.

8.5.5.5 put Client Pvt
void asPutClientPvt(ASCLIENTPVT pvt, void userPvt);

This routine is used to set the pointer returned by asGetClientPvt.

8.5.5.6 register Callback
long asRegisterClientCallback(ASCLIENTPVT pvt, 
                              ASCLIENTCALLBACK pcallback);

This routine registers a callback that will be called whenever the access privilege of the client changes.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.5.7 check Get
long asCheckGet(ASCLIENTPVT pvt);

This routine, actually a macro, returns TRUE if the client has read access rights.

8.5.5.8 check Put
long asCheckPut(ASCLIENTPVT pvt);

This routine, actually a macro, returns TRUE if the client has write access rights.

8.5.5.9 asTrapWriteBefore and asTrapWriteAfter
void asTrapWriteBefore(ASCLIENTPVT clientPvt, 
    const char userid, const char hostid, void serverSpecific); 
void asTrapWriteAfter(void trapPvt);

These routines must be called before and after any write performed for a client, to permit any registered listeners to be notified. The value returned by the call to asTrapWriteBefore is the trapPvt value that must subsequently be passed to the asTrapWriteAfter routine. The serverSpecific argument is assigned to the serverSpecific field of the asTrapWriteMessage described below.

8.5.6 Access Computation

8.5.6.1 compute all Asg
long asComputeAllAsg(void);

This routine calls asComputeAsg for each access security group.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.6.2 compute Asg
long asComputeAsg(ASG pasg);

This routine calculates all CALC entries for the ASG and calls asCompute for each client of each member of the specified access security group.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.6.3 compute access

rights

long asCompute(ASCLIENTPVT pvt);

This routine computes the access rights of a client. This routine is normally called by the access library itself rather than user code.

This routine returns S_asLib_asNotActive without doing anything if access control is not active.

8.5.7 Diagnostics

8.5.7.1 Dump
int asDump(void (⋆member)(ASMEMBERPVT), 
void (⋆client)(ASCLIENTPVT),int verbose); 
int asDumpFP(FILE fp,void (⋆member)(ASMEMBERPVT), 
void (⋆client)(ASCLIENTPVT),int verbose);

These routines print the current access security database. If verbose is 0 (FALSE), then only the information obtained from the access security file is printed.

If verbose is TRUE then additional information is printed. The value of each INP is displayed. The list of members belonging to each ASG and the clients belonging to each member are displayed. If member callback is specified as an argument, then it is called for each member. If client callback is specified, it is called for each access security client.

8.5.7.2 Dump UAG
int asDumpUag(char uagname) 
int asDumpUagFP(FILE fp,char uagname)

These routines display the specified UAG or if uagname is NULL each UAG defined in the access security database.

8.5.7.3 Dump HAG
int asDumpHag(char hagname) 
int asDumpHagFP(FILE fp,char hagname)

These routines display the specified UAG or if uagname is NULL each UAG defined in the access security database.

8.5.7.4 Dump Rules
int asDumpRules(char asgname) 
int asDumpRulesFP(FILE fp,char asgname)

These routines display the rules for the specified ASG or if asgname is NULL the rules for each ASG defined in the access security database.

8.5.7.5 Dump member
int asDumpMem(char asgname, 
        void (⋆memcallback)(ASMEMBERPVT),int clients) 
int asDumpMemFP(FILE fp,char asgname, 
        void (⋆memcallback)(ASMEMBERPVT),int clients)

This routine displays the member and, if clients is TRUE, client information for the specified ASG or if asgname is NULL the member and client information for each ASG defined in the access security database. It also calls memcallback for each member if this argument is not NULL.

8.5.7.6 Dump hash table
int asDumpHash(void) 
int asDumpHash(FILE fp,void)

These show the contents of the hash table used to locate UAGs and HAGs,

8.6 Database Access Security

8.6.1 Access Level definition

The definition of access level means that a level is defined for each field of each record type.

  1. struct dbFldDes in dbBase.h contains a field as_level. In addition definitions are provided for the symbols ASL0 and ASL1.
  2. Each field description in a record description contains a field with the value ASLx.

The meanings of the Access Security Level definitions are as follows:

Most record types assign ASL as follows: The fields VAL, RES (Reset), and CMD use the value ASL0. All other fields use ASL1.

8.6.2 Access Security Group definition

struct dbCommon contains the fields ASG and ASP. ASG (Access Security Group) is a character string. The value can be assigned via a database configuration tool or else a utility could be provided to assign values during ioc initialization. ASP is an access security private field. It contains the address of an ASGMEMBER.

8.6.3 Database Access Library

Two files asDbLib.c and asCa.c implement the interface between IOC databases and access control. They contain the following routines:

8.6.3.1 Initialization
int asSetFilename(char acf);

Calling this routine sets the filename of an access configuration file, but does not save the current working directory, so the use of an absolute pathname is strongly recommended. The next call to asInit uses this filename. asSetFilename must be called before iocInit otherwise access configuration is disabled. Is access security is disabled during iocInit it will never be turned on.

int asSetSubstitutions(char substitutions);

This routine specifies macro substitutions for use while reading the configuration file.

int asInit(); 
int asInitAsyn(ASDBCALLBACK pcallback);

This routines call asInitialize. If the current access configuration file, as specified by asSetFilename, is NULL then the routine just returns, otherwise the configuration file is used to create the access configuration database. After initialization all records in the database are made members of the appropriate access control group.

asInit is called by iocInit, and can also be called after iocInit to change the access configuration information.

asInitAsyn spawns a task asInitTask to perform the initialization. This allows asInitAsyn to be called from a subroutine called by the process entry of a subroutine record. asInitTask calls taskwdInsert so that if it suspends for some reason taskwd can detect the failure.

If the caller provides an ASDBCALLBACK then when either initialization completes or taskwd detects a failure the user’s callback routine is called via one of the standard callback tasks.

asInitAsyn will return a value of -1 if access initialization is already active. It returns 0 if asInitTask is successfully spawned.

8.6.3.2 Routines used by Channel Access Server
int asDbGetAsl(void paddr);

Get Access Security level for the field referenced by a database access structure. The argument is defined as a void⋆ so that both old and new database access can be used.

void  asDbGetMemberPvt(void paddr);

Get ASMEMBERPVT for the field referenced by a database access structure. The argument is defined as a void⋆ so that both old and new database access can be used.

8.6.3.3 Routine to test asAddClient
int astac(char pname,char user,char host);

This is a routine to test asAddClient. It simulates the calls that are made by Channel Access.

8.6.3.4 Subroutines attached to a subroutine record

These routines are provided so that a channel access client can force an ioc to load a new access configuration database.

long asSubInit(struct subRecord prec,int pass); 
long asSubProcess(struct subRecord prec);

These are routines that can be attached to a subroutine record. Whenever a 1 is written to the record, asSubProcess calls asInit. If asInit returns success, it returns asynchronously. When asInitTask calls the completion routine supplied by asSubProcess, the completion status is used to determine whether to place the record in alarm or not.

8.6.3.5 Diagnostic Routines

These routines provide interfaces to the asDump routines described in the previous chapter. They do NOT lock before calling the associated routine. Thus they may fail if the access security configuration is changing while they are running. However the danger of the user accidently aborting a command and leaving the access security system locked is considered a risk that should be avoided.

asdbdump(void)
asdbdumpFP(FILE ⋆fp)

These routines call asDumpFP with a member callback and with verbose TRUE.

aspuag(char ⋆uagname)
aspuagFP(FILE ⋆fp,char ⋆uagname)

These routines call asDumpUagFP.

asphag(char ⋆hagname)
asphagFP(FILE ⋆fp,char ⋆hagname)

These routines call asDumpHagFP.

asprules(char ⋆asgname)
asprulesFP(FILE ⋆fp,char ⋆asgname)

These routines call asDumpRulesFP.

aspmem(char ⋆asgname,int clients)
aspmemFP(FILE ⋆fp,char ⋆asgname,int clients)

These routines call asDumpMemFP.

8.7 Channel Access Security

EPICS Access Security was originally designed to protect Input Output Controllers (IOCs) from unauthorized access via the Channel Access (CA) network protocol. It can also be used by any Channel Access Server (CAS) tool. For example the Channel Access PV Gateway implements its own access security. This section describes the interaction between a CA server and the Access Security system. It also briefly describes how the current access rights state is communicated to clients of the EPICS control system via the CA client interface.

8.7.1 CA Server Interfaces to the Access Security System

The CA server calls asAddClient() and asRegisterClientCallback() for each of the channels that a client connects to the server. The routine asRemoveClient() is called whenever the client clears (removes) a channel or when the client disconnects.

The server maintains storage for the clients host and user names. The initial value of these strings are supplied to the server when the client connects and can be updated at any time by the client. When these strings change then asChangeClient() is called for each of the channels maintained by the server for the client.

The server checks for read access when processing gets and for write access when processing puts. If access is denied an exception message will be sent to the client. The macros asCheckGet() and asCheckPut() perform the checks.

The server checks for read access when processing requests to register an event callback (monitor) for the client. If there is read access the server always sends an initial update indicating the current value. If there isn’t read access the server sends one update indicating no read access and disables subsequent updates.

The server registers a callback with asRegisterClientCallback() in order to receives asynchronous notification of access rights changes. When a channel’s access rights change, the server communicates the current state to the client library. If read access to a channel is lost and there are events (monitors) registered on the channel then the server sends an update to the client for each of them indicating no access and disables future updates for each event. If read access is reestablished to a channel and there are events (monitors) registered on the channel, the server reenables updates and sends an initial update message to the client for each of them.

The server must also call asTrapWriteBefore() and asTrapWriteAfter() before and after a put request from a client is performed.

8.7.2 Client Interfaces

Additional details on the channel access client side callable interfaces to access security can be obtained from the Channel Access Reference Manual.

The client library stores and maintains the current state of the access rights for each channel that it has established. The client library receives asynchronous updates of the current access rights state from the server. It uses this state to check for read access when processing gets and for write access when processing puts. If a program issues a channel access request that is inconsistent with the client library’s current knowledge of the access rights state, the access is denied and an error code is returned to the application. The current access rights state as known by the client library can be tested by an applications program with the C macros ca_read_access() and ca_write_access().

An application program can also receive asynchronous notification of changes to the access rights state by registering a function to be called whenever the client library updates its knowledge of the access rights state. The application’s callback function is installed using ca_replace_access_rights_event().

If the access rights state changes in the server after a request is queued in the client library but before the request is processed by the server, it is possible that the request will fail in the server. Under these circumstances then an exception will be raised in the client.

The server always sends one update to the client when the event (monitor) is initially registered. If there isn’t read access then the status in the arguments to the application program’s event call back function indicates no read access and the value in the arguments to the clients event call back is set to zero. If the read access right changes after the event is initially registered, another update is supplied to the application programs call back function.

8.8 Trapping Channel Access Writes

Access security provides a facility asTrapWrite that can monitor write requests and pass them to any software that registers a listener function. In order to use this facility three things are necessary:

  1. The server using this library must call asTrapWriteBefore() and asTrapWriteAfter(). These routines are defined in asLib.h. The RSRV channel access server running on the IOC makes these calls.
  2. asTrapWrite() gets called by asTrapWriteBefore() and asTrapWriteAfter() and uses the TRAPWRITE option specified with the RULEs given in the access configuration file to decide if listeners should be called. asTrapWrite also includes a routine asTrapWriteRegisterListener().
  3. Some facility not included with access security must call asTrapWriteRegisterListener(). If nothing calls asTrapWriteRegisterListener, asTrapWrite does nothing.

The remainder of this section describes how a facility can use asTrapWrite.h, which is defined as:

typedef struct asTrapWriteMessage { 
    const char userid; 
    const char hostid; 
    void serverSpecific; 
    void userPvt; 
} asTrapWriteMessage; 
 
 
typedef void asTrapWriteId; 
typedef void(⋆asTrapWriteListener)(asTrapWriteMessage pmessage,int after); 
 
asTrapWriteId asTrapWriteRegisterListener(asTrapWriteListener func); 
void asTrapWriteUnregisterListener(asTrapWriteId id);

After a facility calls asTrapWriteRegisterListener() its asTrapWriteListener() will get called before and after each write with an associated RULE that has the option TRAPWRITE set.

asTrapWriteRegisterListener() is passed the address of an asTrapWriteMessage. This message contains the following fields:

asTrapWriteListener delays the associated server thread so it must not do anything that causes it to block.

The IOC’s RSRV server the calls asTrapWriteBefore with serverSpecific set to a dbChannel ⋆ describing the PV.

8.9 Access Control: Implementation Overview

This section provides a few aids for reading the access security code. Include file asLib.h describes the control blocks used by the access security library.

8.9.1 Implementation Overview

The following files form the access security system:

asLib.h

Definitions for the portion of access security that is independent of IOC databases.

asDbLib.h

Definitions for access routines that interface to an IOC database.

asLib_lex.l

Lex and Yacc (actually EPICS flex and antelope) are used to parse the access configuration file. This is the lex input file.

asLib.y

This is the yacc input file. Note that it includes asLibRoutines.c, which do most of the work.

asLibRoutines.c

These are the routines that implement access security. This code has no knowledge of the database or channel access. It is a general purpose access security implementation.

asDbLib.c

This contains the code for interfacing access security to the IOC database.

asCa.c

This code contains the channel access client code that implements the INP and CALC definitions in an access security database.

ascheck.c

The Unix program which performs a syntax check on a configuration file.

8.9.2 Locking

Because it is possible for multiple tasks to simultaneously modify the access security database it is necessary to provide locking. Rather than try to provide low level locking, the entire access security database is locked during critical operations. The only things this should hold up are access initialization, CA searches, CA clears, and diagnostic routines. It should NEVER cause record processing to wait. In addition CA gets and puts should never be delayed. One exception exists. If the ASG field of a record is changed then asChangeGroup is called which locks.

All operations invoked from outside the access security library that cause changes to the internal structures of the access security database.routines lock.

8.10 Structures

PIC