Domain Usage Tracking for Windows NT
by Paul Trout


Listing One
/********************************************************************
* rd_sec_log return values:
*     1 = Success
*   247 = Error locking event log record buffer 
*   248 = Error allocating event log record buffer
*   249 = Error creating .on file
*   250 = Error creating .off file
*   251 = Error allocating ofr buffer
*   252 = Error allocating onr buffer
*   253 = Error getting number of event log records
*   254 = Error opening security event log
*
* Compile: cl /MT /nologo /W3 /D "WIN32" /D "CONSOLE"  
*             /Zp1 /Gz /c rd_sec_log.c
********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <process.h>    /* _beginthreadex & _endthreadex */

#include <windows.h>
#include <winnt.h>
#include <winbase.h>    /* Eventlog,Thread,Sych & Memory Mgmt API */

#include "srvuse.h"     /* Application specific */

/********************************************************************
* Global so it's visible to ALL threads, functions, etc.
********************************************************************/
extern struct control   *ctrl;     /* Array of control structures */

unsigned long int rd_sec_log(unsigned long int *srv) { 
  unsigned char     *tmp0;         /* Convert session id to 2 longs */
  unsigned char     *tmp1;         /* Convert session id to 2 longs */
  unsigned char     *tmp2;         /* Convert session id to 2 longs */
  unsigned char     *evtbuf=NULL;  /* Current event log record */
  unsigned char     *strbeg;       /* Start eventlog entry string */
  unsigned int       rtc=1;        /* Function return code */
  unsigned int       ctr;          /* Scratch FOR loop counter */
  unsigned int       recctr;       /* Event log record counter */
  unsigned int       evtread=0;    /* Bytes read from log */
  unsigned int       evtreq=0;     /* Bytes required next entry */
  unsigned long int  evtrecs;      /* Number of event log records */
  unsigned long int  evtreadflag=0;/* Event log read flags */
  unsigned long int  stroff;       /* String offset event log rec*/
  unsigned long int  bytes_writ=0; /* Bytes written with file write */
  struct onrec      *onr;          /* Logon record buffer */
  struct offrec     *ofr;          /* Logoff record buffer */
  BOOL               tfrtc;        /* True/False return code */
  HANDLE             evtlog;       /* Eventlog handle */
  HANDLE             tmpon;        /* Temporary file for onrecs */
  HANDLE             tmpoff;       /* Temporary file for offrecs */
  HGLOBAL            bufhnd;       /* Buffer memory handle */

  /******************************************************************
  * Open the server's Security Event Log.  If there is an error, 
  *   return a status code of 254.
  ******************************************************************/
  evtlog=OpenEventLog(ctrl[*srv].unc,"Security");
  if(evtlog==NULL) {
    rtc=254;        
    goto RD_SEC_LOG_EXIT;
  }

  /******************************************************************
  * Retrieve the number of event log records.  If there is an error,
  *   return a status code of 253.
  ******************************************************************/
  tfrtc=GetNumberOfEventLogRecords(evtlog,&evtrecs);
  if(tfrtc==FALSE) {              
    rtc=253;                      
    goto RD_SEC_LOG_EXIT;         
  }
  /* Initialize lgrecs field in the proper control structure */
  ctrl[*srv].lgrecs=evtrecs;

  /******************************************************************
  * Allocate a buffer for one logon record.  If there is an error,
  *   return a status code of 252
  ******************************************************************/
  onr=(struct onrec *)GlobalAlloc(GPTR,sizeof(struct onrec));
  if(onr==NULL) {                
    rtc=252;                     
    goto RD_SEC_LOG_EXIT;        
  }

  /******************************************************************
  * Allocate a buffer for one logoff record.  If there is an error,
  *   return a status code of 251
  ******************************************************************/
  ofr=(struct offrec *)GlobalAlloc(GPTR,sizeof(struct offrec));
  if(ofr==NULL) {                
    rtc=251;                     
    goto RD_SEC_LOG_EXIT;        
  }

  /******************************************************************
  * Open a temporary file to hold all of the logoff records.  If 
  *   there is an error, return a status code of 250
  ******************************************************************/
  tmpoff=CreateFile(ctrl[*srv].offname,GENERIC_WRITE,0,NULL,
                    CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  if(tmpoff==INVALID_HANDLE_VALUE) {
    rtc=250;                        
    goto RD_SEC_LOG_EXIT;           
  }

  /******************************************************************
  * Open a temporary file to hold all of the logon records.  If 
  *   there is an error, return a status code of 249
  ******************************************************************/
  tmpon=CreateFile(ctrl[*srv].onfname,GENERIC_WRITE,0,NULL,
                   CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  if(tmpon==INVALID_HANDLE_VALUE) {
    rtc=249;                       
    goto RD_SEC_LOG_EXIT;          
  }

  /******************************************************************
  * Allocate a buffer for one event log record.  If there is an 
  *   error, return a status code of 248
  ******************************************************************/
  bufhnd=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,
                     sizeof(EVENTLOGRECORD));
  if(bufhnd==NULL) {           
    rtc=248;                   
    goto RD_SEC_LOG_EXIT;      
  }

  /* Lock the event record buffer in memory */
  /******************************************************************
  * Lock the event log record in memory.  If there is an error, 
  *   return a status code of 247
  ******************************************************************/
  evtbuf=(unsigned char *)GlobalLock(bufhnd); 
  if(evtbuf==NULL) {             
    rtc=247;                     
    goto RD_SEC_LOG_EXIT;        
  }

  /******************************************************************
  * Process every record in the Security log
  ******************************************************************/
  for(recctr=0; recctr<evtrecs; recctr++) {

    /****************************************************************
    * Set the event log read flags to access log in same order as 
    *   event viewer application
    ****************************************************************/
    evtreadflag=EVENTLOG_SEQUENTIAL_READ|EVENTLOG_BACKWARDS_READ;

    /****************************************************************
    * Read one event log record
    ****************************************************************/
    tfrtc=ReadEventLog(evtlog,evtreadflag,1L,evtbuf,
                       sizeof(EVENTLOGRECORD),&evtread,&evtreq);

    /****************************************************************
    * If there was an error, and the error was caused by the buffer 
    *   being too small, reallocate the buffer to the needed size, 
    *   and retry the read 
    ****************************************************************/
    if(tfrtc==FALSE && GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
      tfrtc=GlobalUnlock(bufhnd);
      bufhnd=GlobalReAlloc(bufhnd,evtreq*sizeof(unsigned char),
                           GMEM_ZEROINIT);
      evtbuf=(unsigned char *)GlobalLock(bufhnd);
      tfrtc=ReadEventLog(evtlog,evtreadflag,1L,evtbuf,
                         evtreq,&evtread,&evtreq);
    }

    /****************************************************************
    * If there was no error, and the record is a logon record 
    * process the data, save it to the onrec buffer, and write 
    * the buffer out to disk 
    ****************************************************************/
    if(((EVENTLOGRECORD *)evtbuf)->EventID==528) {
      stroff=((EVENTLOGRECORD *)evtbuf)->StringOffset;
      strbeg=(((unsigned char *)evtbuf)+stroff);
      onr->ontime=((EVENTLOGRECORD *)evtbuf)->TimeGenerated;
      strcpy(onr->srv,ctrl[*srv].srv);
      for(ctr=0; ctr<7; ctr++) {   /* Logon record has 7 strings */
        if(ctr==0)                 /* User name is first string */
          strcpy(onr->name,strbeg);
        if(ctr==1)                 /* Domain name is second string */
          strcpy(onr->domain,strbeg);

        /************************************************************
        * With the session id, since it gets saved as 2 longs, 
        *   convert it using strtol, after finding the separating 
        *   comma
        ************************************************************/
        if(ctr==2) {               /* Session ID is third string */
          tmp0=strbeg;             /* Point to the ( */
          tmp0++;                  /* And then 1 past it */
          tmp1=strchr(strbeg,','); /* Point to dividing comma */
          tmp1++;                  /* And then 1 past it */
          onr->id0=strtol(tmp0,&tmp2,16);   
          onr->id1=strtol(tmp1,&tmp2,16);   
        }
        if(ctr==6)                     /* Workstation is 7th string */
          if(strbeg[0]=='\\')          /* If the name is UNC name */
            strcpy(onr->wrk,strbeg+2); /* Leave off the \\ */
          else                         /* Otherwise */
            strcpy(onr->wrk,strbeg);   /* Just copy it */
        strbeg=strchr(strbeg,'\0');    /* Point to end this string */
        strbeg++;                      /* Point to beg next string */
      }
      tfrtc=WriteFile(tmpon,onr,sizeof(struct onrec),
                      &bytes_writ,NULL);
      /**************************************************************
      * Zero the record after write
      **************************************************************/
      ZeroMemory(onr,sizeof(struct onrec));
    }

    /****************************************************************
    * Logoff record processing matches logon processing, except the 
    *   ofrec buffer is used, and there is less information in the 
    *   string portion of the record 
    ****************************************************************/
    if(((EVENTLOGRECORD *)evtbuf)->EventID==538) { 
      stroff=((EVENTLOGRECORD *)evtbuf)->StringOffset;
      strbeg=(((unsigned char *)evtbuf)+stroff);
      ofr->offtime=((EVENTLOGRECORD *)evtbuf)->TimeGenerated;
      for(ctr=0; ctr<4; ctr++) {  /* Logoff record has 4 strings */
        if(ctr==2) {              /* Session ID is third string */
          tmp0=strbeg;            /* Point to the ( */
          tmp0++;                 /* And then 1 past it */
          tmp1=strchr(strbeg,',');/* Point to dividing comma */
          tmp1++;                 /* And then 1 past it */
          ofr->id0=strtol(tmp0,&tmp2,16);
          ofr->id1=strtol(tmp1,&tmp2,16);
        }
        strbeg=strchr(strbeg,'\0');/* Point to end this string */
        strbeg++;                  /* Point to beg next string */
      }
      tfrtc=WriteFile(tmpoff,ofr,sizeof(struct offrec),
                      &bytes_writ,NULL);
      ZeroMemory(ofr,sizeof(struct offrec));
    }

    /****************************************************************
    * Unlock the event log record buffer, resize it, and relock it 
    ****************************************************************/
    tfrtc=GlobalUnlock(bufhnd);
    bufhnd=GlobalReAlloc(bufhnd,sizeof(EVENTLOGRECORD),GMEM_ZEROINIT);
    evtbuf=(unsigned char *)GlobalLock(bufhnd);
  }                                    /* End of processing loop */

  /******************************************************************
  * Using a switch case statement for error processing, allows the
  * function to properly terminate no matter where the error occurred
  ******************************************************************/
  RD_SEC_LOG_EXIT:
  switch(rtc) {
    case   1 :
    case 247 : evtbuf=GlobalFree(bufhnd);
    case 248 : tfrtc=CloseHandle(tmpon); 
    case 249 : tfrtc=CloseHandle(tmpoff);
    case 250 : ofr=GlobalFree(ofr);      
    case 251 : onr=GlobalFree(onr);      
    case 252 :
    case 253 : tfrtc=CloseEventLog(evtlog);
    case 254 : _endthreadex(rtc);          
  }
}


