              <<< VAXAXP::NOTES$:[NOTES$LIBRARY]VMSNOTES.NOTE;1 >>>
               -< VAX and Alpha VMS - Digital Internal Use Only >-
================================================================================
Note 770.15    Audit file deletes *not* caused by certain account?      15 of 16
DYPSS1::DYSERT "Barry - Custom Software Developmen" 274 lines  15-MAY-1996 12:41
            -< bug fix: ignore Audits instead of processing Alarms >-
--------------------------------------------------------------------------------
/*  AUDIT_LISTENER

    This program functions as the audit server listener. As such, it receives
    all security messages delivered by the audit server. Currently, its only
    function is to log to the audit journal file all alarms generated by
    file deletes. To enable an alarm on a file so that its deletion generates
    an alarm, set an ACE like this:

	$ SET SECURITY/ACL=(ALARM=SECURITY,ACCESS=DELETE+SUCCESS) file

    Of course, ACL alarms must also be enabled, and the listener armed.
    Also enable the particulars for the audit server on each node:

	$ SET AUDIT/ALARM/ENABLE=ACL
	$ RUN AUDIT_LISTENER
	$ SET AUDIT/LISTENER=AUDIT_LISTENER_MBX

    Note that this program must be run as a detached process, preferably
    as SYSTEM. Also, since the listener only receives messages generated
    on the local node (not to mention the fact that mailboxes are per-node
    devices), this program and the enabling of the listener should be done
    on each node in the cluster.

    The listener is documented (such as it is) in the Guide to System
    Security, Section 9.4.3.2. The format of the message packets is
    documented in the System Management Utilities Reference Manual (A-L),
    Appendix F. The SYS$FORMAT_AUDIT and SYS$AUDIT_EVENT services are of
    course documented in the System Services Reference Manual.

    Author:  Barry D. Dysert, Digital Equipment Corporation

    Created: 13-MAY-1996

	     c h a n g e   l o g

	 date     | name  | description
*******************************************************************************/

#ifdef __DECC
#  include <cmbdef.h>
#  include <nsadef.h>
#else
#  define CMB$M_READONLY 0x1	    /* from STARLET.MLB's $CMBDEF module */
#  include "nsadef.h"		    /* from STARLET.MLB's $NSADEF module */
#endif

#include <descrip.h>
#include <iodef.h>
#include <lib$routines.h>
#include <starlet.h>
#include <stdio.h>
#include <string.h>

#define MAX_MESSAGE 3072    /* that's what the BLISS example uses */
#define PACKET_OFFSET 20    /* used to initialize, since we change it */

main()
    {
    static unsigned int status, audit_status;
    static int pnum;
    static short chan;
    static char message[MAX_MESSAGE];
    static char outbuf[5 * MAX_MESSAGE]; /* arbitrarily larger than `message' */
    static $DESCRIPTOR (outdes, outbuf);
    static $DESCRIPTOR (crlf, "\r\n");
    static $DESCRIPTOR (mbx, "AUDIT_LISTENER_MBX");
    static struct
	{
	short status;
	short count;
	int pid;
	} iosb;
    static struct
	{
	short buflen;
	short code;
	char  *bufadr;
	short *retlenadr;
	} itmlst[12];


    /*	The format for the audit data structures come from Appendix F of
	the System Management Utilities Reference Manual. Under DEC C,
	`nsamsgdef' is defined in the NSADEF.H header file. Seems simplest
	to just declare offsets into the `message' buffer. */

    static int w_record_type = 0;
    static int w_record_subtype = 2;
    static int w_flags = 4;
    static int w_packet_count = 6;
    static int w_record_size = 8;
    static int b_version = 11;
    static int w_facility = 18;

    static int w_packet_size = PACKET_OFFSET;
#define w_packet_type (w_packet_size + 2)
#define a_packet_data (w_packet_size + 4)

    /*	These variables will hold the fixed packet data, moved to the
	properly sized) fields from the `message' buffer */

    static short record_type, record_subtype, flags, packet_count;
    static short record_size, facility, packet_size, packet_type;
    static char version, *packet_data;

    /*	These variables will hold the variable packet data, moved from
	the `message' buffer */

    static int final_status, access_desired, process_id;
    static short file_id[3];
    static char object_class[23], process_name[15], username[32];
    static char file_name[1024];    /* arbitrarily large */

/******************************************************************************/

    /*	Initialize the item list used for the $AUDIT_EVENTW call */

    itmlst[0].code = NSA$_EVENT_TYPE;
    itmlst[0].buflen = sizeof(record_type);
    itmlst[0].bufadr = (char *) &record_type;
    itmlst[0].retlenadr = 0;

    itmlst[1].code = NSA$_EVENT_SUBTYPE;
    itmlst[1].buflen = sizeof(record_subtype);
    itmlst[1].bufadr = (char *) &record_subtype;
    itmlst[1].retlenadr = 0;

    itmlst[2].code = NSA$_AUDIT_NAME;
    itmlst[2].buflen = strlen("SECURITY");
    itmlst[2].bufadr = "SECURITY";
    itmlst[2].retlenadr = 0;

    itmlst[3].code = NSA$_FINAL_STATUS;
    itmlst[3].bufadr = (char *) &final_status;
    itmlst[3].retlenadr = 0;

    itmlst[4].code = NSA$_ACCESS_DESIRED;
    itmlst[4].bufadr = (char *) &access_desired;
    itmlst[4].retlenadr = 0;

    itmlst[5].code = NSA$_OBJECT_CLASS;
    itmlst[5].bufadr = (char *) &object_class;
    itmlst[5].retlenadr = 0;

    itmlst[6].code = NSA$_FILE_ID;
    itmlst[6].bufadr = (char *) &file_id;
    itmlst[6].retlenadr = 0;

    itmlst[7].code = NSA$_PROCESS_ID;
    itmlst[7].bufadr = (char *) &process_id;
    itmlst[7].retlenadr = 0;

    itmlst[8].code = NSA$_PROCESS_NAME;
    itmlst[8].bufadr = (char *) &process_name;
    itmlst[8].retlenadr = 0;

    itmlst[9].code = NSA$_USERNAME;
    itmlst[9].bufadr = (char *) &username;
    itmlst[9].retlenadr = 0;

    itmlst[10].code = NSA$_FILE_NAME;
    itmlst[10].bufadr = (char *) &file_name;
    itmlst[10].retlenadr = 0;

    itmlst[11].code = 0;
    itmlst[11].buflen = 0;

    /*	Create the (permanent) mailbox, named AUDIT_LISTENER_MBX. Set its
	protection mask so that only System has access (since that's who
	the Audit Server is). We also indicate we're only a reader. */

    if (((status = sys$crembx(1,&chan,MAX_MESSAGE,0,0xfff0,0,&mbx,
	CMB$M_READONLY)) & 1) != 1) lib$stop(status);

    /*	Loop forever, reading whatever is written to the mailbox */

    while (((status = sys$qiow(0,chan,IO$_READVBLK,&iosb,0,0,
	message,sizeof(message),0,0,0,0)) & 1) == 1)
	{
	if ((iosb.status & 1) != 1) lib$stop(iosb.status);

	/*  Process the types we're interested in, and ignore the rest.
	    We only care about Alarms generated by Deletes. Note that we
            ignore the alarm if Audit is set instead of doing processing
            if Alarm is set. This is because if I generate an Audit of
    	    an event that has Alarms enabled, it comes back to me with
    	    both Audit and Alarm set (infinite loop). */

	memmove(&flags,&message[w_flags],sizeof(flags));
        if ((flags & NSA$M_AUDIT) != 0) continue;  /* has Audit set; ignore */

	memmove(&record_type,&message[w_record_type],sizeof(record_type));
	switch (record_type)
	    {

	    case NSA$C_MSG_OBJ_DELETE:
		memmove(&record_subtype,&message[w_record_subtype],sizeof(record_subtype));
		if (record_subtype != NSA$C_OBJ_DELETE) break;

#ifdef format_audit
		if (((status = sys$format_audit(NSA$C_FORMAT_STYLE_FULL,
		    &message,&outdes.dsc$w_length,&outdes,0,&crlf,0,0)) & 1) != 1)
		    lib$stop(status);
		if (((status = lib$put_output(&outdes)) & 1) != 1)
		    lib$stop(status);
#endif

		memmove(&packet_count,&message[w_packet_count],sizeof(packet_count));
		w_packet_size = PACKET_OFFSET;
		for (pnum = 1; pnum <= packet_count; pnum++)
		    {
		    memmove(&packet_type,&message[w_packet_type],sizeof(packet_type));
		    memmove(&packet_size,&message[w_packet_size],sizeof(packet_size));
		    packet_data = &message[a_packet_data];

		    switch (packet_type)
			{
			case NSA$_FINAL_STATUS:
			    memmove(&final_status,packet_data,packet_size - 4);
			    itmlst[3].buflen = packet_size - 4;
			    break;
			case NSA$_ACCESS_DESIRED:
			    memmove(&access_desired,packet_data,packet_size - 4);
			    itmlst[4].buflen = packet_size - 4;
			    break;
			case NSA$_OBJECT_CLASS:
			    memmove(&object_class,packet_data,packet_size - 4);
			    itmlst[5].buflen = packet_size - 4;
			    break;
			case NSA$_FILE_ID:
			    memmove(&file_id,packet_data,packet_size - 4);
			    itmlst[6].buflen = packet_size - 4;
			    break;
			case NSA$_PROCESS_ID:
			    memmove(&process_id,packet_data,packet_size - 4);
			    itmlst[7].buflen = packet_size - 4;
			    break;
			case NSA$_PROCESS_NAME:
			    memmove(&process_name,packet_data,packet_size - 4);
			    itmlst[8].buflen = packet_size - 4;
			    break;
			case NSA$_USERNAME:
			    memmove(&username,packet_data,packet_size - 4);
			    itmlst[9].buflen = packet_size - 4;
			    break;
			case NSA$_FILE_NAME:
			    memmove(&file_name,packet_data,packet_size - 4);
			    itmlst[10].buflen = packet_size - 4;
			    break;
			default:
			    break;
			}   /* switch (packet_type) */

		    w_packet_size += packet_size;
		    }	/* for (pnum) */

		/*  Write the alarm to the audit journal */

		if (((status = sys$audit_eventw(0,NSA$M_MANDATORY,&itmlst,
		    &audit_status,0,0)) & 1) != 1) lib$stop(status);
		if ((audit_status & 1) != 1) lib$stop(audit_status);

		break;

	    default:
		break;

	    }	/* switch (record_type) */

	}   /* while */

    lib$stop(status);

    }
