This chapter describes access security, i.e. the system that limits access to IOC databases. It consists of the following sections:
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.
In order to ``turn on" access security for a particular IOC the following must be done:
iocInit
via a subroutine record with asSubInit
and
asSubProcess
as the associated subroutines.
Writing the value 1 to this record will cause a reload.
asSetFilename("/full/path/to/accessSecurityFile")
asSetSubstitutions("var1=sub1,var2=sub2,...")
The following rules decide if access security is turned on for an IOC:
asSetFilename
is not executed before iocInit, access security will never be started.
asSetFile
is given and any error occurs while first initializing access security, then all access to that ioc is denied.
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.
Access security protects IOC databases from unauthorized Channel Access Clients. Access security is based on the following:
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.
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.
This document uses the following terms:
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.
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
.
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>) } ... }] ...
DEFAULT
is a special case. If a member specifies a null group or a
group which has no ASG definition then the member is assigned to the group DEFAULT
.
A
to L
. These are just like the INP
fields of a
calculation record. It is necessary to define INP
fields if a CALC
field is defined in any RULE
for the ASG.
<level>
must be 0 or 1. Permission for a level 1 field implies
permission for level 0 fields. The permissions are NONE
, READ
, and WRITE
. WRITE
permission implies
READ
permission. The standard EPICS record types have all fields set to level 1 except for VAL
, CMD
(command), and RES
(reset). An optional argument specifies if writes should be trapped. See the section
below on trapping Channel Access writes for how this is used. If not given the default is NOTRAPWRITE.
CALC
field of a calculation record except that the result must evaluate to TRUE
or FALSE
. The rule only applies if the calculation result is TRUE
, where the actual test for TRUE
is
(0.99 < result < 1.01)
. Anything else is regarded as FALSE
and will cause the rule to be
ignored. Assignment statements are not permitted in CALC expressions here.
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:
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.
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.
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.
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
.
Two subroutines, which can be attached to a subroutine record, are available (provided with iocCore
):
asSubInit asSubProcessNOTE: 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:
asSetFilename
so that it contains the new configuration desired.
VAL
field. Note that this can be done via channel access.
The following action is taken:
asInit
is called and the value set back to 0.
BRSV
.
Each field of each record type has an associated access security level of ASL0
or ASL1
. See the chapter ``Database
Definition" for details.
Lets design a set of rules for a Linac. Assume the following:
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:
LI:OPSTATE
and any other records that need tighter control have ASG="critical
". One such record could be
a subroutine record used to cause a new access configuration file to be loaded. LI:OPSTATE
has the value (0,1)
if the Linac is (not operational, operational).
LI:lev1permit
has ASG="permit"
. In order for the opSup
, linacSup
, or an appDev
to have write
privilege to everything this record must be set to the value 1.
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) } }
A brief summary of the Functional Requirements is:
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.
Access security should be implemented as a stand alone system, i.e. it should not be imbedded tightly in database or channel access.
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.
It must be possible to easily define default access rules.
When an IOC is initialized, access security is optional.
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.
User access groups, host access groups, and access security groups are configured via an ASCII file.
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.
The interface between an IOC database and the access security system.
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
.
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.
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:
caput
) was created that performs ca_put
s on each of the 5000 channels. Each time it
begins a new set of puts the value increments by 1.
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.
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.
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);
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 ASG
s.
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.
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.
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.
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
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.
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.
This code is called by a channel access server.
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.
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.
long asRemoveClient(ASCLIENTPVT *pvt);
This routine returns S_asLib_asNotActive
without doing anything if access control is not active.
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.
void asPutClientPvt(ASCLIENTPVT pvt, void *userPvt);
This routine is used to set the pointer returned by asGetClientPvt
.
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.
long asCheckGet(ASCLIENTPVT pvt);
This routine, actually a macro, returns TRUE
if the client has read access rights.
long asCheckPut(ASCLIENTPVT pvt);
This routine, actually a macro, returns TRUE
if the client has write access rights.
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.
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.
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.
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.
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.
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.
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.
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.
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
.
int asDumpHash(void) int asDumpHash(FILE *fp,void)
These show the contents of the hash table used to locate UAG
s and HAG
s,
The definition of access level means that a level is defined for each field of each record type.
struct dbFldDes
in dbBase.h
contains a field as_level
.
In addition definitions are provided for the symbols ASL0
and ASL1
.
ASLx
.
The meanings of the Access Security Level definitions are as follows:
ASL0
Assigned to fields used during normal operation
ASL1
Assigned to fields that may be sensitive to change.
Permission to access this level implies permission for ASL0
.
Most record types assign ASL as follows:
The fields VAL
, RES
(Reset), and CMD
use the value ASL0
.
All other fields use ASL1
.
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
.
Two files asDbLib.c
and asCa.c
implement the interface between IOC databases and access control.
They contain the following routines:
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.
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.
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.
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.
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
.
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.
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.
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.
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:
asTrapWriteBefore()
and asTrapWriteAfter()
.
These routines are defined in asLib.h.
The RSRV channel access server running on the IOC makes these calls.
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()
.
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:
userid
- Userid of whoever originated the request.
hostid
- Hostid of whoever originated the request.
serverSpecific
- The meaning of this field is server specific.
If the listener uses this field it must know what type of server is supplying the messages.
It is the value the server provides to asTrapWriteBefore
.
userPvt
- This field is for use by the asTrapWriteListener
.
When the listener is called before the write, userPvt
has the value 0.
The listener can give it any value it desires and userPvt
will have have the same value when the listener gets called after the write.
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.
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.
The following files form the access security system:
Definitions for the portion of access security that is independent of IOC databases.
Definitions for access routines that interface to an IOC database.
Lex
and Yacc
(actually EPICS flex
and antelope
) are used to parse the access configuration file. This is the lex
input file.
This is the yacc
input file. Note that it includes asLibRoutines.c
, which do most of the work.
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.
This contains the code for interfacing access security to the IOC database.
This code contains the channel access client code that implements the INP
and CALC
definitions in an access security database.
The Unix program which performs a syntax check on a configuration file.
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.