[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP OpenVMS Programming Concepts Manual


Previous Contents Index

33.4.4 Principal Names

So long as there is no targeting by the caller of the SYS$ACM[W] system service (discussed in Section 33.4.5), the decision regarding which ACME agent handles a particular request is governed by the following factors:

  • The ordering of ACME agents selected by the system manager
  • The syntax of the principal name
  • The spelling of the principal name

If the syntax provided to the SYS$ACM[W] system service can be handled by only one ACME agent, that settles the matter. If it can be handled by more than one ACME agent, then the decision also depends on which ACME agent (in order) is the first to be able to map the particular principal name to a VMS user name.

Whether a particular ACME agent can map a particular principal name also depends on the mapping tables or algorithms specific to that ACME agent, but this is typically more time-consuming than simple decisions made on the basis of the syntax presented in the principal name. Consider the acceptable syntax presented in the following table:

Domain of Interpretation Principal Name Syntax
VMS username
Windows NT domain\user OR user@domain OR user

Given those two ACME agents, it is possible to specify a principal name that can only be handled by the Windows NT DOI (by a full specification including the execute (@) command), but it is not possible to specify a principal name that can only be handled by the VMS DOI.

But that table only describes the situation for the combination of those two ACME agents, the initial ones produced by HP. The VMS ACME is always present on any OpenVMS system, but on some systems you might omit the NT ACME and/or include some other ACME agents, one of which might honor some of the same syntax as the NT ACME agent.

33.4.5 Targeting Your System Service Calls

Most Authenticate Principal and Change Password calls are handled by one or more ACME agents chosen in accordance with selection criteria set by the system manager.

Your calling program can specify a target DOI using one of the following item codes:

  • ACME$_TARGET_DOI_ID
  • ACME$_TARGET_DOI_NAME

These item codes are used when your program requires that a particular DOI handle your request.

33.4.5.1 DOI Names

The following two DOI names supplied by HP are currently defined:

Domain of Interpretation Name Source of the ACME Agent
VMS VMS OpenVMS
Windows NT MSV1_0 Advanced Server

33.4.5.2 When to Use DOI_NAME vs. DOI_ID

The following item codes affect the SYS$ACM[W] system service operations in the same way:

  • ACME$_TARGET_DOI_ID
  • ACME$_TARGET_DOI_NAME

A similar relationship exists between the following item codes:

  • ACME$_CONTEXT_ACME_ID
  • ACME$_CONTEXT_ACME_NAME

The system manager specifies DOI names in configuring the ACME server, although in most cases the system manager uses the registered names specified by a vendor.

DOI IDs are implicitly specified by the system manager by the order in which each is specified for the first time after each boot of the system. That means that a particular DOI ID may have an entirely different meaning on the same machine after the next reboot.

Specifying a DOI_NAME clearly gives better ease-of-use, while specifying a DOI_ID gives slightly better performance with an overhead penalty paid up front to look up a DOI_ID based on a DOI_NAME. Some programs that call the SYS$ACM[W] system service, however, need to perform that lookup in order to interpret the contents of the ACM communications buffer, so in those cases the DOI_ID is already available and can be used in calls to the SYS$ACM[W] system service.

33.4.5.3 Looking Up DOI and ACME IDs

Use the Query function code with a Target DOI ID of 0 (meaning the SYS$ACM[W] system service itself) to determine what DOI_ID corresponds to a given name.

The item list to do this would be as follows:

  • SYS$ACM[W] server query - ID value 0:
    ITMCOD = ACME$_TARGET_DOI_ID
    BUFSIZ = 4
    BUFADR = Address of longword containing 0
  • Query based on ACME name:
    ITMCOD = ACME$_QUERY_KEY_TYPE
    BUFSIZ = 4
    BUFADR = Address of longword containing ACME$K_QUERY_ACME_NAME
  • Specify ACME name:
    ITMCOD = ACME$_QUERY_KEY_VALUE
    BUFSIZ = Characters in ACME name (times 4 if setting ACME$M_UCS2_4)
    BUFADR = Address of buffer containing ACME name
  • Specify ACME ID as the return value:
    ITMCOD = ACME$_QUERY_TYPE
    BUFSIZ = 4
    BUFADR = Address of longword containing ACME$K_QUERY_ACME_ID
  • Specify the output buffer
    ITMCOD = ACME$_QUERY_DATA
    BUFSIZ = 4
    BUFADR = Address of longword to receive the ACME_ID

33.4.6 Determining ACME Information with the Query Function

The general nature of the Query function is that your code supplies the following items:

  • ACME$_TARGET_DOI_ID (or ACME$_TARGET_DOI_NAME)
  • ACME$_QUERY_TYPE
  • ACME$_QUERY_KEY_TYPE
  • ACME$_QUERY_KEY_VALUE

Your program receives back the item ACME$_QUERY_DATA.

Semantics of those items and where the data comes from is entirely up to the ACME agent that you specify as the target of the Query function.

See the documentation for that ACME agent for more information.

33.4.7 Reporting an Event

The general nature of the Event function is that your code supplies the following items:

  • ACME$_EVENT_TYPE
  • ACME$_EVENT_DATA_IN

Your program possibly receives back item ACME$_EVENT_DATA_OUT. Whether ACME$_EVENT_DATA_OUT is supported and the exact nature of what the SYS$ACM[W] system service is supposed to do for an event is up to the ACME agent that you specify as the target of the Event function.

See the documentation for that ACME agent for more information.

33.5 Authentication Scenarios

You can use the SYS$ACM[W] system service to accomplish the following functions:

  • Authenticate a specified user.
  • Change a password.
  • Reauthenticate the current user.
  • Create a process on behalf of a user.

It was possible to perform many of these functions prior to introduction of the SYS$ACM[W] system service by combining the use of the following techniques:

  • SYS$GETUAI
  • SYS$SETUAI
  • SYS$HASH_PASSWORD
  • SYS$SCAN_INTRUSION
  • Modal restriction enforcement (network, batch, interactive, and so on)
  • Checks for account disabled, account expired, and so on

These steps made it difficult to provide a complete and bug-free implementation. Furthermore, such an approach dealt only with traditional VMS password-based authentication rather than including add-on mechanisms. With the introduction of the SYS$ACM[W] system service, those scenarios can be handled in a uniform, supported manner.

33.5.1 Simple User Authentication

If all information is known in advance, a call to SYS$ACMW is quite simple, as in the following example:


        LOCAL
            STATUS,
            ACME_STATUS_BLOCK : VECTOR [4,LONG],
            NON_DIALOGUE_ITMLST : ALIAS_ON_AXP $ITMLST_DECL (ITEMS=2);
        !
        ! Populate that item list
        !
        $ITMLST_INIT(ITMLST = NON_DIALOGUE_ITMLST,
             !
             ! What is the Principal Name
             !
             (ITMCOD = ACME$_PRINCIPAL_NAME_IN,
              BUFSIZ = %CHARCOUNT('JENKINS'),
              BUFADR = UPLIT BYTE('JENKINS')),
             !
             ! What Password was given to this routine ?
             !
             (ITMCOD = ACME$_PASSWORD_1,
              BUFSIZ = .INPUT_STRING [DSC$W_LENGTH],
              BUFADR = .INPUT_STRING [DSC$A_POINTER] ) );
        !
        ! Now call the System Service
        !
        STATUS = $ACMW (EFN=EFN$C_ENF,
                        FUNC=ACME$_FC_AUTHENTICATE_PRINCIPAL,
                        ITMLST=NON_DIALOGUE_ITMLST,
                        ACMSB=ACME_STATUS_BLOCK );

33.5.2 Evaluating Status Codes

After any call to the SYS$ACM[W] system service, you must check the return status from the call and the primary status in the ACM Status Block. Following is a sample check:


IF NOT .STATUS
THEN
    SIGNAL_STOP ( STATUS );

IF NOT .ACME_STATUS_BLOCK [ACMESB$L_STATUS]
AND ( .ACME_STATUS_BLOCK [ACMESB$L_STATUS] NEQ ACME$_OPINCOMPL )
THEN
    BEGIN
    IF .ACME_STATUS_BLOCK [ACMESB$L_ACME_ID] NEQ 0
    THEN
        REPORT_ACME_SPECIFIC_ERROR ( ACME_STATUS_BLOCK )
    ELSE
        IF .ACME_STATUS_BLOCK [ACMESB$L_SECONDARY_STATUS]
           EQL .ACME_STATUS_BLOCK [ACMESB$L_STATUS]
        THEN
            SIGNAL_STOP (
                .ACME_STATUS_BLOCK [ACMESB$L_STATUS] )
        ELSE
            SIGNAL_STOP (
                .ACME_STATUS_BLOCK [ACMESB$L_STATUS], 0,
                .ACME_STATUS_BLOCK [ACMESB$L_SECONDARY_STATUS], 0 );
    END;

The details of handling the field ACMESB$L_ACME_STATUS depend on the nature of the ACME agent indicated in field ACMESB$L_ACME_ID. If that ACME agent is not specifically known to the program that calls the SYS$ACM[W] system service, there is no way to interpret that field. The previous example presumes there is special knowledge regarding at least one ACME agent held in routine REPORT_ACME_SPECIFIC_ERROR, which is not supplied.

33.5.3 Password Change Dialogue

Particularly with the function code ACME$_FC_CHANGE_PASSWORD, you cannot reliably predict all the necessary input at the time of the initial call, because the first password chosen might be found in the password history file or be unacceptable in some other way.

Following is a sample of how you might decode and process a dialogue response:


BIND
    ACMECB = .CONTEXT : BLOCK[,BYTE],
    ITEM_SET = .ACMECB[ACMECB$PS_ITEM_SET] : BLOCKVECTOR[,ACMEIS$K_LENGTH,BYTE],
    RESPONSE_ITEM_COUNTER : INITIAL [0],
    !
    ! A real program should calculate the size for the following
    ! by basing it on .ACMECB[ACMECB$L_ITEM_SET_COUNT].
    !
    RESPONSE_ITEM_LIST : ALIAS_ON_AXP $ITMLST_DECL (ITEMS=9999);
!
! Store a terminator in case there are no input items.
!
RESPONSE_ITEM_LIST [0,ITM$L_TERMINATOR] = 0;
!
! Iterate over Itemset Array
!
INCRU ITEM_SET_INDEX FROM 1 TO .ACMECB[ACMECB$L_ITEM_SET_COUNT] DO
    BEGIN
    BIND
        ITEM_SET_ENTRY = ITEM_SET [.ITEM_SET_INDEX,0,0,0,0],
        ITEM_FLAGS = ITEM_SET_ENTRY [ACMEIS$L_FLAGS] : BLOCK[4,BYTE],
        ITEM_CODE = ITEM_SET_ENTRY [ACMEIS$W_ITEM_CODE] : BLOCK[2,BYTE];
    IF NOT .ITEM_CODE[ACMEIC$V_UCS]
    THEN
        SIGNAL_STOP ( THIS_PROGRAM_HANDLES_ONLY_TEXT );
    IF NOT .ITEM_CODE[ACMEIC$V_OUTPUT]
    THEN
        BEGIN   ! Respond to an input item
        !
        ! Call subroutines to read input and put it in the item list.
        !
        IF .ITEM_FLAGS[ACMEDLOGFLG$V_NOECHO]
        THEN
            !
            ! Read the input - Last parameter (if any) indicates the prompt
            ! to be used on a confirmation read.  That confirmation must
            ! match the initial response before returning here.
            !
            CONSTRUCT_ITEM_NOECHO_FROM_TERMINAL (
                 RESPONSE_ITEM_LIST [.RESPONSE_ITEM_COUNTER,0,0,0,0],
                 .ITEM_SET_ENTRY [acmeis$w_max_length],
                 ITEM_SET_ENTRY [acmeis$q_data_1],
                 ITEM_SET_ENTRY [acmeis$q_data_2] )
        ELSE
            !
            ! Just read the input - Last parameter indicates a default
            ! that will be taken by SYS$ACM if a blank line is supplied.
            !
            CONSTRUCT_ITEM_FROM_TERMINAL (
                 RESPONSE_ITEM_LIST [.RESPONSE_ITEM_COUNTER,0,0,0,0],
                 .ITEM_SET_ENTRY [acmeis$w_max_length],
                 ITEM_SET_ENTRY [acmeis$q_data_1],
                 ITEM_SET_ENTRY [acmeis$q_data_2] );
        !
        ! Advance past this item.
        !
        RESPONSE_ITEM_COUNTER = .RESPONSE_ITEM_COUNTER + 1;
        !
        ! Store a terminator in case this was the last input item.
        !
        RESPONSE_ITEM_LIST [.RESPONSE_ITEM_COUNTER,ITM$L_TERMINATOR] = 0;
        BEGIN
    END;
!
! Now call the System Service again, with the same FUNC argument.
!
! If all the item set entries were for output, we send a null item list.
!
STATUS = $ACMW (EFN=EFN$C_ENF,
                FUNC=ACME$_FC_CHANGE_PASSWORD,
                CONTXT=CONTEXT,
                ITMLST=RESPONSE_ITEM_LIST,
                ACMSB=ACMESB );

This example leaves the details of handling an optional confirmation prompt to the routine CONSTRUCT_ITEM_NOECHO_FROM_TERMINAL, which is not supplied. In addition, the code to process and display output items is not shown.

Confirmation prompts are more common in Change Password than in Authenticate Principal, but the program that calls SYS$ACM[W] system service should be prepared to handle them in either situation (that is, any time dialogue mode is used).

33.5.4 Reauthentication of Current User

The following code illustrates what you might do within an application to ensure that the same user was still at the terminal. An example of reauthentication would be a check-writing application that requires reauthentication for any check over a certain value.


        LOCAL
            STATUS,
            ACME_STATUS_BLOCK : VECTOR [4,LONG],
            NON_DIALOGUE_ITMLST : ALIAS_ON_AXP $ITMLST_DECL (ITEMS=1);
        !
        ! Populate that item list
        !
        $ITMLST_INIT(ITMLST = NON_DIALOGUE_ITMLST,
             !
             ! What Password was given to this routine ?
             !
             (ITMCOD = ACME$_PASSWORD_1,
              BUFSIZ = .INPUT_STRING [DSC$W_LENGTH],
              BUFADR = .INPUT_STRING [DSC$A_POINTER] ) );
        !
        ! Now call the System Service
        !
        STATUS = $ACMW (EFN=EFN$C_ENF,
                        FUNC=ACME$_FC_AUTHENTICATE_PRINCIPAL
                             +ACME$M_DEFAULT_PRINCIPAL,
                        ITMLST=NON_DIALOGUE_ITMLST,
                        ACMSB=ACME_STATUS_BLOCK );

33.5.5 Manipulating Personas

The following example is a slight variant on the example in Section 33.5.1:


        LOCAL
            STATUS,
            OLD_PERSONA,
            NEW_PERSONA,
            ACME_STATUS_BLOCK : VECTOR [4,LONG],
            NON_DIALOGUE_ITMLST : ALIAS_ON_AXP $ITMLST_DECL (ITEMS=3);
        !
        ! Populate that item list
        !
        $ITMLST_INIT(ITMLST = NON_DIALOGUE_ITMLST,
             !
             ! What is the Principal Name
             !
             (ITMCOD = ACME$_PRINCIPAL_NAME_IN,
              BUFSIZ = %CHARCOUNT('JENKINS'),
              BUFADR = UPLIT BYTE('JENKINS')),
             !
             ! What Password was given to this routine ?
             !
             (ITMCOD = ACME$_PASSWORD_1,
              BUFSIZ = .INPUT_STRING [DSC$W_LENGTH],
              BUFADR = .INPUT_STRING [DSC$A_POINTER] ) ),
             !
             ! Where do we want the new Persona ID ?
             !
             (ITMCOD = ACME$_PERSONA_HANDLE_OUT,
              BUFADR = NEW_PERSONA ) );
        !
        ! Now call the System Service
        !
        STATUS = $ACMW (EFN=EFN$C_ENF,
                        FUNC=ACME$_FC_AUTHENTICATE_PRINCIPAL
                             +ACME$M_ACQUIRE_CREDENTIALS,
                        ITMLST=NON_DIALOGUE_ITMLST,
                        ACMSB=ACME_STATUS_BLOCK );
        CHECK_STATUS ( .STATUS );
        CHECK_ACME_STATUS ( ACME_STATUS_BLOCK );
        !
        ! Now assume the new Persona
        !
        STATUS = $PERSONA_ASSUME (PERSONA=NEW_PERSONA,
                                  FLAGS=0,
                                  previous=OLD_PERSONA);
        !

This example does not use item code ACME$_PERSONA_HANDLE_IN. That is for manipulating the characteristics of an existing persona, which is not the objective of this example.

33.5.6 Using CREPRC on Behalf of a User

After authentication, you can use the SYS$ACM[W] system service to create a process on behalf of the user, with process quotas set according the values in SYSUAF, as in the following example:


        LOCAL
            STATUS,
            OLD_PERSONA,
            NEW_PERSONA,
            PIDADR,
            CREPRC_QUOTA : VECTOR [255,BYTE],   ! hopefully long enough
            ACME_STATUS_BLOCK : VECTOR [4,LONG],
            NON_DIALOGUE_ITMLST : ALIAS_ON_AXP $ITMLST_DECL (ITEMS=4);
        !
        ! Populate that item list
        !
        $ITMLST_INIT(ITMLST = NON_DIALOGUE_ITMLST,
             !
             ! What is the Principal Name
             !
             (ITMCOD = ACME$_PRINCIPAL_NAME_IN,
              BUFSIZ = %CHARCOUNT('JENKINS'),
              BUFADR = UPLIT BYTE('JENKINS')),
             !
             ! What Password was given to this routine ?
             !
             (ITMCOD = ACME$_PASSWORD_1,
              BUFSIZ = .INPUT_STRING [DSC$W_LENGTH],
              BUFADR = .INPUT_STRING [DSC$A_POINTER] ) ),
             !
             ! Where do we want the new persona ID?
             !
             (ITMCOD = ACME$_PERSONA_HANDLE_OUT,
              BUFADR = NEW_PERSONA),
             !
             ! Where do we want quota requirements stored ?
             !
             (ITMCOD = ACMEVMS$_CREPRC_QUOTA,
              BUFSIZ = %ALLOCATION(CREPRC_QUOTA),
              BUFADR = CREPRC_QUOTA ) );
        !
        ! Now call the System Service
        !
        STATUS = $ACMW (EFN=EFN$C_ENF,
                        FUNC=ACME$_FC_AUTHENTICATE_PRINCIPAL,
                        ITMLST=NON_DIALOGUE_ITMLST,
                        ACMSB=ACME_STATUS_BLOCK );
        CHECK_STATUS ( .STATUS );
        CHECK_ACME_STATUS ( ACME_STATUS_BLOCK );
        !
        ! That routine just detected any buffer too short error
        !
        ! Temporarily assume the new Persona
        !
        STATUS = $PERSONA_ASSUME (PERSONA=NEW_PERSONA,
                                  FLAGS=0,
                                  previous=OLD_PERSONA);
        CHECK_STATUS ( .STATUS );
        !
        ! Now create the process under that persona
        !
        STATUS = $CREPRC (PIDADR=PIDADR,
                          IMAGE=$DESCRIPTOR('SYS$SYSTEM:LOGINOUT'),
                          INPUT=$DESCRIPTOR('TXA3:'),
                          OUTPUT=$DESCRIPTOR('TXA3:'),
                          ERROR=$DESCRIPTOR('TXA3:'),
                          QUOTA=CREPRC_QUOTA,
                          STSFLG=PRC$M_NOUAF+PRC$M_INHERIT_PERSONA);
        CHECK_STATUS ( .STATUS );
        !
        ! Revert to our old Persona
        !
        STATUS = $PERSONA_ASSUME (PERSONA=OLD_PERSONA);
        CHECK_STATUS ( .STATUS );
        !
        ! Delete the new persona from this process
        !
        STATUS = $PERSONA_DELETE (PERSONA=NEW_PERSONA );
        CHECK_STATUS ( .STATUS );
        !

The PRC$M_NOUAF flag prevents LOGINOUT from modifying process quotas, while the PRC$M_INHERIT_PERSONA prevents LOGINOUT from modifying the process persona, allowing use of the persona (and persona extension) of the parent process. The main purpose of LOGINOUT in this case becomes setting up the DCL environment.

33.6 Authentication Examples

This section provides two complete examples of using the SYS$ACM[W] system service. In addition to these examples, a utility program called ACMEUTIL is available for issuing authentication and change-password SYS$ACM system service calls and examining the results in both dialogue and nondialogue mode.

You interact with the ACMEUTIL utility using the DCL interface. For example, to issue a dialogue request for authentication, use the following syntax:


$acme auth/dial=(input,noecho)

See the comments in ACMEUTIL_SETUP.COM for additional information on ACMEUTIL DCL syntax and capabilities.

ACMEUTIL is located in the SYS$EXAMPLES directory and is built by running the ACMEUTIL.COM procedure. To define the DCL verb ACMEUTIL, run the ACMEUTIL_SETUP.COM procedure.

33.6.1 Example Using Nondialogue Mode (C)

This theoretical example supports a hardware badge reader and provides all necessary authentication information with a single call to the SYS$ACM[W] system service. The simple linear programming style used here would also be appropriate for a situation in which that authentication information is received from a network connection using a fixed protocol that does not allow queries back to the originator.

Line Activity Special Notes
45 Declare local storage This subroutine avoids static storage.
105 Determine MAXBUF MAXBUF limits hardware interaction.
105 Determine MAXBUF MAXBUF limits hardware interaction.
158 Prepare a SYS$ACM item list We will add data as we go.
210 Prepare to use the badge reader Subsequent steps use it.
233 Compare DNA Invoking a hardware function.
264 Read the principal name Data from the badge reader.
298 Read the primary password Data from the badge reader.
324 Read the secondary password Data from the badge reader.
357 Free the badge reader We do not know when image will exit.
372 Call SYS$ACMW Here is where we authenticate.
389 Return status to our caller We choose to provide no details.


     1  #pragma module ACM_BADGE "V1.0"
     2  /*
     3  ** ACM_BADGE.C
     4  */
     5  #define         __NEW_STARLET   1
     6
     7  #include        <string>                // NULL and memset
     8  #include        <starlet.h>             // for calling SYS$ACM
     9  #include        <iledef.h>              // Item Lists
    10  #include        <iodef.h>               // for calling $QIO
    11  #include        <iosbdef.h>             // IO status blocks
    12  #include        <stsdef>                // decoding status code fields
    13  #include        <efndef>                // For event flag number definitions
    14  #include        <descrip.h>             // for descriptor definitions
    15  #include        <utcblkdef.h>           // required for acmedef.h
    16  #include        <acmedef.h>             // ACME codes
    17  #include        <syidef.h>              // GETSYI codes
    18
    19  /*
    20  **      ACM_BADGE
    21  **
    22  **      This subroutine obtains a user name and password from a
    23  **      hardware badge reader and passes them in a nondialogue call to
    24  **      SYS$ACM for evaluation.  It returns success or failure status
    25  **      to its caller.
    26  **
    27  **      Obviously the hardware badge reader construction must be secure
    28  **      enough to never divulge the password without validating a DNA
    29  **      sample from the person who places the badge in the reader,
    30  **      and that is why this is just a code sample in SYS$EXAMPLES:
    31  **      rather than something that comes with a real hardware product.
    32  **
    33  **      Arguments:
    34  **
    35  **          None.
    36  **
    37  **      Return values:
    38  **
    39  **              ACME$_NORMAL - authentic
    40  **              ACME$_AUTHFAILURE - not authentic
    41  **              anything else - processing failure
    42  */
    43  int ACM_BADGE()
    44  {
    45      int                         RetStatus;
    46      int                         DasStatus;
    47      char                        devnam[5]="BRA0:";
    48      struct dsc$descriptor_s     devnam_desc = { 0,              // length
    49                                                  DSC$K_DTYPE_T,  // type
    50                                                  DSC$K_CLASS_S,  // class
    51                                                  0};             // buf address
    52      IOSB                        iosb;
    53      ACMESB                      acmsb;
    54      unsigned short int          badge_reader_channel;
    55      int                         logon_type = ACME$K_NETWORK;
    56      int                         maxbuf;
    57      int                         read_size;
    58
    59      /*
    60      ** We will call SYS$ACM with multiple entries in an item list.
    61      */
    62      enum acm_items
    63          {
    64              acm_logon_type,
    65              acm_principal_name_in,
    66              acm_password_1,
    67              acm_password_2,
    68              acm_terminator
    69          };
    70      ILE3                        acm_itmlst[acm_terminator+1];
    71      enum getsyi_items
    72          {
    73              getsyi_maxbuf,
    74              getsyi_terminator
    75          };
    76      ILE3                        getsyi_itmlst[getsyi_terminator+1];
    77
    78      /*
    79      ** Our badge reader might provide very long input items,
    80      ** because a user does not have to type them.  Internally
    81      ** the items will be carried in Unicode format, so the
    82      ** length limit imposed by the 16-bit length field is
    83      ** 1/4 of what one might first expect.
    84      **
    85      ** Internal limits on the size of a single SYS$ACM request
    86      ** actually constrain the total of all items to this size,
    87      ** but we cannot predict how imbalanced the item lengths
    88      ** will be, so we make all buffers be this maximum size.
    89      **
    90      ** Depending on the value of system parameter MAXBUF, this
    91      ** subroutine may try to read as many as buffer_size bytes
    92      ** from the badge reader, so the Buffered IO Byte Limit
    93      ** quota should be as large as buffer_size if the badge
    94      ** reader is not a Direct IO device.
    95      */
    96      enum sizes
    97          {
    98              buffer_size = 65535/4
    99          };
   100      char                        principal_name_in[buffer_size];
   101      char                        password_1[buffer_size];
   102      char                        password_2[buffer_size];
   103
   104
   105      /*
   106      ** Get the SYS$GETSYI item list ready. First zero it out, then fill it in.
   107      */
   108      memset (                                    // clear out memory
   109          getsyi_itmlst,                          // - Address to write to
   110          0,                                      // - Character to fill
   111          sizeof (getsyi_itmlst));                // - size to fill
   112
   113      /*
   114      ** System Parameter MAXBUF constrains the size of Buffered IO
   115      **
   116      ** Buffer maxbuf will be filled in by SYS$GETSYI.
   117      */
   118      getsyi_itmlst[getsyi_maxbuf].ile3$w_code = SYI$_MAXBUF;
   119      getsyi_itmlst[getsyi_maxbuf].ile3$w_length = sizeof (maxbuf);
   120      getsyi_itmlst[getsyi_maxbuf].ile3$ps_bufaddr = &maxbuf;
   121      getsyi_itmlst[getsyi_maxbuf].ile3$ps_retlen_addr = NULL;
   122
   123      /*
   124      ** End the item list with a terminator
   125      */
   126      getsyi_itmlst[getsyi_terminator].ile3$w_code = 0;
   127      getsyi_itmlst[getsyi_terminator].ile3$w_length = 0;
   128      getsyi_itmlst[getsyi_terminator].ile3$ps_bufaddr = NULL;
   129      getsyi_itmlst[getsyi_terminator].ile3$ps_retlen_addr = NULL;
   130
   131      RetStatus = sys$getsyiw (
   132                  EFN$C_ENF,              /* no event flag */
   133                  NULL,                   /* CSID address */
   134                  NULL,                   /* Node Name */
   135                  &getsyi_itmlst,         /* Item List */
   136                  &iosb,                  /* IO Status block */
   137                  NULL,                   /* AST routine */
   138                  0 );                    /* AST Parameter */
   139      if (RetStatus & STS$M_SUCCESS)
   140          {
   141          /*
   142          ** We use the smaller of maxbuf or the buffer size
   143          */
   144          if (maxbuf < buffer_size)
   145              {
   146              read_size = maxbuf;
   147              }
   148          else
   149              {
   150              read_size = buffer_size;
   151              }
   152          }
   153      else
   154          {
   155          return RetStatus;
   156          }
   157
   158      /*
   159      ** Get the SYS$ACM item list ready. First zero it out, then fill it in.
   160      */
   161      memset (                                    // clear out memory
   162          acm_itmlst,                             // - Address to write to
   163          0,                                      // - Character to fill
   164          sizeof (acm_itmlst));                   // - size to fill
   165
   166      /*
   167      ** Buffer logon_type contains a constant ACME$K_NETWORK.
   168      **
   169      ** Using an interactive Logon Type would subject us to password
   170      ** change requests, which are not viable in nondialogue mode (or
   171      ** from our hypothetical badge reader, for that matter).
   172      */
   173      acm_itmlst[acm_logon_type].ile3$w_code = ACME$_LOGON_TYPE;
   174      acm_itmlst[acm_logon_type].ile3$w_length = sizeof (logon_type);
   175      acm_itmlst[acm_logon_type].ile3$ps_bufaddr = &logon_type;
   176      acm_itmlst[acm_logon_type].ile3$ps_retlen_addr = NULL;
   177
   178      /*
   179      ** Buffer principal_name_in will be filled in from the badge reader.
   180      */
   181      acm_itmlst[acm_principal_name_in].ile3$w_code = ACME$_PRINCIPAL_NAME_IN;
   182      acm_itmlst[acm_principal_name_in].ile3$w_length = maxbuf;
   183      acm_itmlst[acm_principal_name_in].ile3$ps_bufaddr = &principal_name_in;
   184      acm_itmlst[acm_principal_name_in].ile3$ps_retlen_addr = NULL;
   185
   186      /*
   187      ** Buffer password_1 will be filled in from the badge reader.
   188      */
   189      acm_itmlst[acm_password_1].ile3$w_code = ACME$_PASSWORD_1;
   190      acm_itmlst[acm_password_1].ile3$w_length = maxbuf;
   191      acm_itmlst[acm_password_1].ile3$ps_bufaddr = &password_1;
   192      acm_itmlst[acm_password_1].ile3$ps_retlen_addr = NULL;
   193
   194      /*
   195      ** Buffer password_2 will be filled in from the badge reader.
   196      */
   197      acm_itmlst[acm_password_2].ile3$w_code = ACME$_PASSWORD_2;
   198      acm_itmlst[acm_password_2].ile3$w_length = maxbuf;
   199      acm_itmlst[acm_password_2].ile3$ps_bufaddr = &password_2;
   200      acm_itmlst[acm_password_2].ile3$ps_retlen_addr = NULL;
   201
   202      /*
   203      ** End the item list with a terminator
   204      */
   205      acm_itmlst[acm_terminator].ile3$w_code = 0;
   206      acm_itmlst[acm_terminator].ile3$w_length = 0;
   207      acm_itmlst[acm_terminator].ile3$ps_bufaddr = NULL;
   208      acm_itmlst[acm_terminator].ile3$ps_retlen_addr = NULL;
   209
   210      /*
   211      ** Assign a channel to the Badge Reader.
   212      */
   213
   214      devnam_desc.dsc$w_length = sizeof(devnam);
   215      devnam_desc.dsc$a_pointer = &devnam[0];
   216      RetStatus = sys$assign (
   217                  &devnam_desc,           /* Device Name */
   218                  &badge_reader_channel,  /* channel to badge reader */
   219                  0,                      /* access mode */
   220                  0 );                    /* Mailbox Name */
   221      if (!(RetStatus & STS$M_SUCCESS))
   222          {
   223          return RetStatus;
   224          }
   225
   226      /*
   227      ** Exit from this one pass loop to deassign the channel and return.
   228      */
   229
   230      do
   231          {
   232
   233          /*
   234          ** Have the reader compare DNA.
   235          **
   236          ** A failed DNA comparison from the hardware is reported
   237          ** to the user the same as any other authentication failure,
   238          ** withholding details regarding what went wrong.
   239          */
   240
   241          RetStatus = sys$qiow (
   242                      EFN$C_ENF,          /* no event flag */
   243                      badge_reader_channel,       /* channel to badge reader */
   244                      IO$_ACCESS,         /* IO function code */
   245                      &iosb,              /* IO Status block */
   246                      NULL,               /* AST routine */
   247                      0,                  /* AST Parameter */
   248                      0,                  /* p1 */
   249                      0,                  /* p2 */
   250                      0,                  /* p3 */
   251                      0,                  /* p4 */
   252                      0,                  /* p5 */
   253                      0 );                /* p6 */
   254          if (RetStatus & STS$M_SUCCESS)
   255              {
   256              RetStatus = iosb.iosb$w_status;
   257              }
   258          if (!(RetStatus & STS$M_SUCCESS))
   259              {
   260              RetStatus = ACME$_AUTHFAILURE;      /* hide the exact cause */
   261              continue;   /* exit from the do loop */
   262              }
   263
   264          /*
   265          ** Read the Principal Name.
   266          */
   267
   268          RetStatus = sys$qiow (
   269                      EFN$C_ENF,          /* no event flag */
   270                      badge_reader_channel,       /* channel to badge reader */
   271                      IO$_READVBLK,       /* IO function code */
   272                      &iosb,              /* IO Status block */
   273                      NULL,               /* AST routine */
   274                      0,                  /* AST Parameter */
   275                      &principal_name_in, /* Buffer address */
   276                      read_size,          /* Buffer length */
   277                      0,                  /* p3 */
   278                      0,                  /* p4 */
   279                      0,                  /* p5 */
   280                      0 );                /* p6 */
   281          if (RetStatus & STS$M_SUCCESS)
   282              {
   283              RetStatus = iosb.iosb$w_status;
   284              }
   285          if (RetStatus & STS$M_SUCCESS)
   286              {
   287              acm_itmlst[acm_principal_name_in].ile3$w_length = iosb.iosb$w_bcnt;
   288              }
   289          else
   290              {
   291              continue;   /* exit from the do loop */
   292              }
   293
   294          /*
   295          ** Read the Primary Password.
   296          */
   297
   298          RetStatus = sys$qiow (
   299                      EFN$C_ENF,          /* no event flag */
   300                      badge_reader_channel,       /* channel to badge reader */
   301                      IO$_READVBLK,       /* IO function code */
   302                      &iosb,              /* IO Status block */
   303                      NULL,               /* AST routine */
   304                      0,                  /* AST Parameter */
   305                      &password_1,        /* Buffer address */
   306                      read_size,          /* Buffer length */
   307                      0,                  /* p3 */
   308                      0,                  /* p4 */
   309                      0,                  /* p5 */
   310                      0 );                /* p6 */
   311          if (RetStatus & STS$M_SUCCESS)
   312              {
   313              RetStatus = iosb.iosb$w_status;
   314              }
   315          if (RetStatus & STS$M_SUCCESS)
   316              {
   317              acm_itmlst[acm_password_1].ile3$w_length = iosb.iosb$w_bcnt;
   318              }
   319          else
   320              {
   321              continue;   /* exit from the do loop */
   322              }
   323
   324          /*
   325          ** Read the Secondary Password.
   326          */
   327
   328          RetStatus = sys$qiow (
   329                      EFN$C_ENF,          /* no event flag */
   330                      badge_reader_channel,       /* channel to badge reader */
   331                      IO$_READVBLK,       /* IO function code */
   332                      &iosb,              /* IO Status block */
   333                      NULL,               /* AST routine */
   334                      0,                  /* AST Parameter */
   335                      &password_2,        /* Buffer address */
   336                      read_size,          /* Buffer length */
   337                      0,                  /* p3 */
   338                      0,                  /* p4 */
   339                      0,                  /* p5 */
   340                      0 );                /* p6 */
   341          if (RetStatus & STS$M_SUCCESS)
   342              {
   343              RetStatus = iosb.iosb$w_status;
   344              }
   345          if (RetStatus & STS$M_SUCCESS)
   346              {
   347              acm_itmlst[acm_password_2].ile3$w_length = iosb.iosb$w_bcnt;
   348              }
   349          else
   350              {
   351              continue;   /* exit from the do loop */
   352              }
   353
   354          }
   355      while (1 == 2);
   356
   357      /*
   358      ** Deassign the channel to the Badge Reader.
   359      */
   360
   361      DasStatus = sys$dassgn (
   362                  badge_reader_channel ); /* channel to badge reader */
   363      if (RetStatus & STS$M_SUCCESS)
   364          {
   365          RetStatus = DasStatus;
   366          }
   367      if (!(RetStatus & STS$M_SUCCESS))
   368          {
   369          return RetStatus;
   370          }
   371
   372      /*
   373      ** Attempt authentication.
   374      */
   375
   376      RetStatus = sys$acmw (
   377                  EFN$C_ENF,              /* no event flag */
   378                  ACME$_FC_AUTHENTICATE_PRINCIPAL, /* ACM function code */
   379                  NULL,                   /* pointer to Context pointer */
   380                  &acm_itmlst,            /* Item List */
   381                  &acmsb,                 /* ACM Status block */
   382                  NULL,                   /* AST routine */
   383                  0 );                    /* AST Parameter */
   384      if (RetStatus & STS$M_SUCCESS)
   385          {
   386          RetStatus = acmsb.acmesb$l_status;
   387          }
   388
   389      return RetStatus;                              // return with ACM status
   390  }


Previous Next Contents Index