#define SCCSID "@(#)Security.c"
/********************************************************/
/*                                                      */
/*       IBM Confidential                               */
/*                                                      */
/*       Copyright (c) IBM Corporation 1987, 1989       */
/*       Copyright (c) Microsoft Corporation 1987, 1989 */
/*                                                      */
/*       All Rights Reserved                            */
/********************************************************/
/*
 * As a possible future enhancement, may want to change the fReentrant
 * stuff when we encounter the first callout. It is a waste to do this
 * save/restore stuff when there is no one to callout to.
 *
 * @108011 1/19/95  LIN - The path name we passed in to the ExecPgm (pre,
 *                     and post) was not always canonacalized (full path).
 *                     We moved the callout to the pre hook in Exec.asm
 *                     down after the child is loaded so we could pull the
 *                     path/file name from the child PTDA. (let loader
 *                     canonacalize the name).  Now we pass the ISS the
 *                     ptr from the ptda, instead of using the tkexecpgm
 *                     input parm.  Difference is the ptr from ptda is
 *                     already linear, so we nixed the virttolin call
 *                     in both SecExec, and SecExecPost.
 * @SEC    3/15/95  LRN - added more callouts and audit hooks.
 * @SEC    4/02/95  LRN - change Sec32Devioctl to probe user addresses
 *                        before invoking the callout.
 *         4/02/95  LRN - changed SecFindNextHlp to SecHlpFindNext to have
 *                        more consistent naming convention and also because
 *                        secure.h had this inconsistently defined.
 *
 * 72417  12/22/95  MLM - Moved from variables from PTDA to TCB so we can
 *                        reduce FSCRIT serialization.  The move also made
 *                        SavePTDA and RestorePTDA obsolete- there was nothing
 *                        in them that needed saving anymore.
*/

#define FNO_NUM FNO_SECURITY_C
#define INCL_TYPES                      // for basedef.h typedefs
#define INCL_DOSERRORS                  // for ERROR_ defines

#include <osconst.h>
#include <ostypes.h>
#include <fnofs.h>
#include <osmac.h>
#include <basemac.h>
#include <basedef.h>
#include <mi.h>
#include <vmexport.h>
#include <vmowner.h>
#include <selxport.h>
#include <pgexport.h>
#include <tkexport.h>
#include <ioexp.h>                      // for _IOSftxxxx calls
#include <bseerr.h>                     // for ERROR_ defines

// Definitions for PTDA vars (WFP_START)
#include <infoseg.h>
#include <except.h>
#include <dirent.h>
#include <curdir.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <sigdef.h>
#include <dossymc.h>
#include <tkptda.h>

#include <smexport.h>
#include <sft.h>
#include <mft.h>

#include <secure2.h>

// The following are needed by Sec32AuditHook
#include <dispatch.h>
#include <sesrqpkt.h>
#include <splist.h>
#include <sesctxt.h>
#ifdef TDLL
#include <tdll.h>
#endif
#include <instance.h>

#define FP_COMMAND_MULTITRACK   0x03        // From dmioctl.h
#define COMMAND_00              0x00
#define COMMAND_01              0x01
#include <dskioctl.h>
#include <ioctl.h>

#include <secgtinf.h>
#include <ldrxport.h>                 // For _KsemQueryMutex           132809

#pragma  code_seg("HIGH32SWAP_TEXT","CODE")

extern PULONG apdemSVC;
extern void SecHook32CG(ULONG pSec32Worker);  // For hooking CallGates
extern void SecHook16CG(ULONG pSec16Worker);  // For hooking CallGates
extern ChgFilePtr(ULONG SFN, LONG Offset, LONG Type, PULONG pAbsolute);
extern SFTFromSFN(ULONG SFN);
extern FindNextHlp(ULONG ulSrchHandle, PUSHORT pResultCnt,
               ULONG Level, PCHAR pResultBuf, ULONG BufSize);
extern FindNextHlp2(ULONG ulSrchHadnle, PUSHORT pResultCnt, PCHAR pResultBuf,
                              ULONG BufSize, ULONG Position, char * pszfile);
ULONG  CallType SecPathFromSFN(ULONG SFN);

ULONG  pSec32Worker;                  // Save address of CallGate Handler
ULONG  pSec16Worker;                  // Save address of CallGate Handler

ULONG  fSec       = 0;                // Global Security Flag
ULONG  fImport    = 0;                // Flag to disable SetImport devhlp
ULONG  fImportPDF = 0;                // Flag to disable SetPDFImport devhlp
ULONG  fImportAV  = 0;                // Flag to disable SetAVImport devhlp

// If the security subsystem wants to call security helpers while
// processing a security event, PTDA variables can get munched.
char fReentrant = TRUE;

pSecurityImport2 pSecIm_list_head = NULL;    // For Import struc linked list


// Internal Function Headers
ULONG GetCurSFN();
ULONG ExtractSFN( sf_entry * pSFT);
void RestoreTCB ( PBYTE Buffer);      // Quick routines to save/restore
void SaveTCB ( PBYTE Buffer);         //   non-reentrant filesys vars
/* void SavePTDA ( PPTDAVARS p ); Obsolete  72417 */
/* void RestorePTDA( PPTDAVARS p);Obsolete  72417 */
ULONG ProbeIt ( PUCHAR pBuffer,
                ULONG  ulLength,
                BOOL   bReadWrite );


/*
 ** StartLock32
 *
 *  Fake routine to mark start of code that must be locked
 *  for swapper operations.
*/
void StartLock32 ( void)
{
   return;
}

#ifdef FSSTRICT
#define ASSERT_NOSWAP AssertNoSwap()
void AssertNoSwap( void )
{
   if (pTCBCur->TCBfSwapping) {
      panic("swappable code called during swapper operation: AssertNoSwap");
      _asm int 3;
   }
}
#else
#define ASSERT_NOSWAP
#endif

/*
 ** Sec32ReadPre  -- Call Security Subsystem  and verify read
 *
 *  This routine calls the security subsystem (at this point
 *  we know it has been loaded) and do a pre-read verification).
 *
 *
 *  Entry
 *     [TCBDMAAddr]  - Adress of Buffer
 *     SFN           - SFN of request
 *     Bytes         - Number of Bytes Requested
 *
*/
ULONG Sec32ReadPre ( ULONG SFN, ULONG Bytes)
{
   pSecurityImport2 ptempSecIm;

   BYTE    TCBVars[TCBIOCopySize];
   PUCHAR  pBuffer;
   tcb_t   *pTCB;
   ULONG   RC = 0;


   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
      // No ptda variables seem to be in danger here
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ((ptempSecIm != NULL ) && (RC == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.READ_PRE != 0) {
          pTCB = pTCBCur;

          // First Convert the DMAAddress to something we can use
          pBuffer = (PUCHAR) SELVirtToLin((USHORT)(pTCB->TCBDMAAdd),
                                             (SEL)((pTCB->TCBDMAAdd) >>16));
          // Call subsystem
          RC = (*ptempSecIm->SecImp.READ_PRE)(SFN,pBuffer,Bytes);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
   }
   return RC;
}



/*
 ** Sec32ReadPost -- Call Security Subsystem after read
 *
 *  Call Security subsystem and let them decrypt user buffer
 *
 *  Entry
 *     [TCBDMAAddr]  - Adress of Buffer
 *     [TCBThisSFT]  - SFT
 *     Bytes         - Number of Bytes User Got
 *     RC            - RC users going to get
 *
*/
void Sec32ReadPost ( ULONG RC, ULONG Bytes)
{
   pSecurityImport2 ptempSecIm;
   PUCHAR   pBuffer;
   ulong_t  SFN;
   tcb_t    *pTCB;



   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return;

   // Rentrancy shouldn't be a problem as the API is already complete

   //  Check with each registered subsystem.
   ptempSecIm = pSecIm_list_head;
   while ( ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.READ_POST != NULL ) {
         pTCB = pTCBCur;
         // First Convert the DMAAddress to something we can use
         pBuffer = (PUCHAR) SELVirtToLin((USHORT)(pTCB->TCBDMAAdd),
                                            (SEL)((pTCB->TCBDMAAdd) >>16));
         SFN  = GetCurSFN();
         (*ptempSecIm->SecImp.READ_POST)( SFN,pBuffer, Bytes,RC);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }
}

/*
 ** Sec32WritePre ( BufSiz )
 *
 *  Allows security subsystem validation of a write event
 *
 *  Entry -
 *    [TCBDMAAddr]   -> Start of User Buffer (16:16)
 *    [TCBThisSFT]   -> Current SFT
 *    BufSize        -> Size of Use Buffer
 *
 *  Returns:   AX =  0 -> let the call proceed
 *             AX != 0 -> Abort the call
 *
*/
ULONG Sec32WritePre  (ULONG BufSiz)
{

   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];
   ULONG    SFN;
   PUCHAR   pBuffer;
   tcb_t    *pTCB;
   ULONG    rc = 0;


   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);
   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
      // No ptda variables seem to be in danger here
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.WRITE_PRE != NULL) {
         pTCB = pTCBCur;
         // First Convert the DMAAddress to something we can use
         pBuffer = (PUCHAR) SELVirtToLin((USHORT)(pTCB->TCBDMAAdd),
                                            (SEL)((pTCB->TCBDMAAdd) >>16));
         SFN = GetCurSFN();
         rc = (*ptempSecIm->SecImp.WRITE_PRE)(SFN,pBuffer,BufSiz);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
   }

   return rc;
}

/*
 ** Sec32WritePost (RC, BytesWritten, BufSiz)
 *
 *  Notifies security subsystem that a write event has finished
 *
 *  Entry -
 *     [TCBDMAAddr]  -> Start of User Buffer
 *     [TCBThisSFT]  -> SFT of current file
 *     RC            -> RC user is going to get w/o out intervention
 *     BytesWritten  -> number of bytes sucsessfully written
 *     BufSiz        -> Byte size of initial write request
 *
 *  Returns:  VOID
*/
void Sec32WritePost ( ULONG RC, ULONG BytesWritten, ULONG BufSiz)
{
   pSecurityImport2 ptempSecIm;
   PUCHAR   pBuffer;
   tcb_t    *pTCB;


   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return;

   //  Check with each registered subsystem.

   ptempSecIm = pSecIm_list_head;
   while  ( ptempSecIm != NULL )  {

      if ((ULONG)ptempSecIm->SecImp.WRITE_POST != NULL ) {
         pTCB = pTCBCur;
         // First Convert the DMAAddress to something we can use
         pBuffer = (PUCHAR) SELVirtToLin((USHORT)(pTCB->TCBDMAAdd),
                                            (SEL)((pTCB->TCBDMAAdd) >>16));
         (*ptempSecIm->SecImp.WRITE_POST)( GetCurSFN(), pBuffer, BufSiz,
                              BytesWritten, RC);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While
}

/*
 ** Sec32ChgFilePtr
 *
 *  Notification of ChgFilePtr, with a chance to modify Values
 *
 *  Entry:
 *
 *
*/
void Sec32ChgFilePtr ( ptr_t   SFT,       // 16:16 ptr to SFT
                       PLONG   pOffset,    // Ptr to Offset
                       PUSHORT pType,      // Ptr to Seek Type
                       PLONG   pAbs,       // Ptr to abs seek offset
                       PLONG   pLogical    // Ptr to Logical seek offset
                      )
{
   pSecurityImport2 ptempSecIm;
   sf_entry   *pSFT;
   ULONG      SFN;
   char       TCBJfnFlag;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return;

   if (fReentrant) {
      TCBJfnFlag = pTCBCur->TCBJfnFlag;
   }

   //  Check with each registered subsystem.

   ptempSecIm = pSecIm_list_head;
   while ( ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.CHGFILEPTR != NULL) {
          pSFT = (sf_entry*) SELVirtToLin(SFT.ptr_off, SFT.ptr_sel);
          SFN  = ExtractSFN(pSFT);

          (*ptempSecIm->SecImp.CHGFILEPTR)(SFN,pOffset,pType,pAbs,pLogical);

          // We now need to patch up the Absolute pointer based on
          // seek type and offset

          // BUGBUG!!  multiple subsystems... Suppose the first guy
          //           dinks with pAbs??

          #ifdef ISVS_SHOULD_DO_THIS
          switch (*pType) {
          case FILE_CURRENT:
               *pAbs = (LONG)( pSFT->sf_fsi.sfi_position + *pOffset);
             break;
          case FILE_END:
               *pAbs = (LONG)( pSFT->sf_fsi.sfi_size + *pOffset);
               // This may turn out to be less than zero.
               // Not a problem since worker will reject the
               // call if it is negative
             break;
          case FILE_BEGIN :
               *pAbs = *pOffset;
             break;
          }
          #endif
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

       if (fReentrant) {
          pTCBCur->TCBJfnFlag = TCBJfnFlag;
       }

}


/*
 ** Sec32NewSize
 *
 *  Validation of DosSetFileSize
 *
 *  Entry:
 *      Size  =  pSFT->sf_fsi-sfi_position
 *      pSFT  -> 16:16 ptr to SFT
 *
*/
ULONG Sec32NewSize    (
                        ptr_t   SFT
                      )

{
   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];
   sf_entry *pSFT;
   ULONG    SFN;
   ULONG    Size;
   ULONG    rc = 0;


   if (fReentrant) {
     SaveTCB(SSToDS(TCBVars));
     // No ptda variables seem to be in danger here
   }
   pSFT = (sf_entry*) SELVirtToLin(SFT.ptr_off, SFT.ptr_sel);
   SFN  = ExtractSFN(pSFT);
   Size = pSFT->sf_fsi.sfi_position;

   ptempSecIm = pSecIm_list_head;
   while  ( ptempSecIm != NULL )  {

      if ((ULONG)ptempSecIm->SecImp.SETFILESIZE != NULL ) {
          rc = (*ptempSecIm->SecImp.SETFILESIZE)(SFN,SSToDS(&Size));
      }
      ptempSecIm = ptempSecIm->pNextSecIm;
   }

  // BUGBUG!! multiple subsystems... Suppose the first guy dinks with Size??

   pSFT->sf_fsi.sfi_position = Size;

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
   }

   return(rc);
}

/*
 ** SaveTCB
 *
 *  Copy TCBVariables to temp buffer
 *
*/
void SaveTCB ( PBYTE Buffer)
{
   tcb_t *pTCB;
   pTCB = pTCBCur;
   memcpy(Buffer, &pTCB->TCBIOCopyStart,TCBIOCopySize);

}
/*
 ** SavePTDA
 *
 *  Save PTDA variables.  These are mostly the PTDA vars that are
 *  set by transpath.  If an oddball ptda variable needs to be
 *  saved for a specific api, the callout should save it.  Not
 *  this routine
 *
 */
 /*  72417
  * All of these variables have been moved to the TCB and will
  *  be saved by SaveTCB, and SaveTCB is always called before
  *  this routine, so this not needed anymore
  */
/* void SavePTDA ( PPTDAVARS p)
 *{
 *   p->WFP_Start       = pPTDACur->WFP_Start;
 *   p->WFP_Path_End    = pPTDACur->WFP_Path_End;
 *   p->Curr_Dir_End    = pPTDACur->Curr_Dir_End;
 *   p->SAttrib         = pPTDACur->Sattrib;
 *   p->PathNameType    = pPTDACur->PathNameType;
 *   p->pSrchBuf        = pPTDACur->ptda_pSrchBuf;
 *   p->cMeta           = pPTDACur->cMeta;
 *
 *   return;
 *}
 */
/*
 ** RestorePTDA
 *
 *  Restore PTDA variables
 *
*/
 /*  72417
  * All of these variables have been moved to the TCB and will
  *  be saved by SaveTCB, and SaveTCB is always called before
  *  this routine, so this not needed anymore
  */
/*void RestorePTDA ( PPTDAVARS p)
 *{
 *   pPTDACur->WFP_Start       =    p->WFP_Start;
 *   pPTDACur->WFP_Path_End    =    p->WFP_Path_End;
 *   pPTDACur->Curr_Dir_End    =    p->Curr_Dir_End;
 *   pPTDACur->Sattrib         =    p->SAttrib;
 *   pPTDACur->PathNameType    =    p->PathNameType;
 *   pPTDACur->ptda_pSrchBuf   =    p->pSrchBuf;
 *   pPTDACur->cMeta           =    p->cMeta;
 *
 *   return;
 *}
 */
/*
 ** RestoreTCB
 *
 *  Restore TCBVariables from temp Buffer
 *
*/
void RestoreTCB ( PBYTE Buffer)
{
   tcb_t *pTCB;
   pTCB = pTCBCur;
   memcpy(&pTCB->TCBIOCopyStart,Buffer,TCBIOCopySize);

}

/*
 ** GetCurSFN
 *
 * Returns SFN number from [TCBThisSFT]
 *
*/
ULONG GetCurSFN ( )
{
   sf_entry *pSFT;
   tcb_t    *pTCB;

   pTCB = pTCBCur;

   if (pTCB->TCBThisSFT == 0) {
      return(0);
   }

   pSFT = (sf_entry*) SELVirtToLin((USHORT)(pTCB->TCBThisSFT),
                                      (SEL)((pTCB->TCBThisSFT) >> 16));
   return ExtractSFN(pSFT);
}
/*
 ** ExtractSFN
 *
 *  Returns the SFN number embeded in the SFT.  This is
 *  a routine so that I can handle the -1 SFNs that come
 *  in from FCB operations.
*/
ULONG ExtractSFN( sf_entry * pSFT)
{
   ULONG  ulSFN;
   ulSFN = (ULONG) pSFT->sf_fsi.sfi_selfsfn;
   if ( pSFT->sf_fsi.sfi_selfsfn == SFN_NULL) {
      _asm int 3;
      ulSFN = 0xffffffff;
   }
   return(ulSFN);
}

/*
 ** SecHlpTrustedPathNotify
 *
 *  Notification of a CTRL-ALT-DEL
 *
 *
 *  Entry
 *    For now we aren't passing any parms to the security device
 *    driver.  This is only a notification of a CTRL-ALT-DEL.
 *
 *  Note that despite its name, this is not really a SecHlp, but rather
 *  just code which does a callout.
 *
 *
*/
ULONG SecHlpTrustedPathNotify (VOID)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;

   //  Check with each registered subsystem.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.TRUSTEDPATHCONTROL != NULL) {
         rc = (*ptempSecIm->SecImp.TRUSTEDPATHCONTROL)();
         if (rc) rc = 1;
      }

      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   return rc;
}
/*
 ***************************************************************************
 ***************************************************************************
 ***************************************************************************
 ** Exported Security Helper Routines
 *
 *
 *  The following routines provide a Security Device Driver
 *  with the capability to do some basic file operations.
 *
 *  The following flags are defined in SECURITY.H
 *
 *    TCBSEC_NOCALLOUT - security.c
 *                       Used to skip calling the security subsystem if we
 *                       came through here by way of a SecurityHelper func.
 *
 *    TCBSEC_NOCHECK   - disk.asm  and gshare.asm
 *                       Used to skip access mode check in fsd_read and
 *                       mode bits in ASC Add to SFT Chain.
 *
 *    TCBSEC_NOLOCKSFT - util.asm
 *                       Used in SFLock and SFUnlock to skip locking the SFT
 *                       if we came by way of a SecurityHelper.  The current
 *                       thread would have already done a lock.
 *
 ***************************************************************************
 *
 *  105379  12/4/94      Set NOLOCKSFT unconditionally for read,write,qfs,
 *                       and chgfileptr.  ISV's using sechlpers on different
 *                       thread of different process (bouncing up to R3)
 *                       so check to determine "current" sfn failed.
 *                       BUGBUG.  This is a temp. fix until issue reviewed
 *
*/


ULONG CallType SecRead  (ULONG SFN,PULONG pcbBytes, PUCHAR pBuffer,ULONG p16Addr, ULONG Offset);
ULONG CallType SecWrite (ULONG SFN, PULONG pcbBytes,PUCHAR pBuffer,ULONG p16Addr, ULONG Offset);
ULONG CallType SecOpen  (PSZ pszFileName,PULONG pSFN,ULONG ulOpenFlag,ULONG ulOpenMode);
ULONG CallType SecClose (ULONG SFN);
ULONG CallType SecQFileSize (ULONG SFN, PULONG pSize);
ULONG CallType SecChgFilePtr(ULONG SFN, LONG Offset, LONG Type, PULONG pAbsolute);
ULONG CallType SecSFFromSFN(ULONG SFN);
ULONG CallType SecHlpFindNext( PFINDPARMS pParms);

/*
 ** SecOpen
 *
 *  Security Helper SecOpen.  Allows the security subsystem to open
 *  a file.
 *
 *  Entry..
 *
 *
*/
ULONG _syscall SecOpen ( PSZ pszFileName,         // File to open
             PULONG pSFN,                         // ptr  to SFN
             ULONG  ulOpenFlag,
             ULONG  ulOpenMode)
{
   ULONG rc;
   tcb_t    *pTCB;

   /************************************************************************/
   /* This code was added to be constant with the Ring 0 workers    103892 */
   /* which will not be called  by this routine.             ARJ    103892 */
   /************************************************************************/
   /* Bits 8-31 are reserved, 5-7 and 2-3 are unused.               103892 */
   /* Invalid Value  for bits 0-3 are 3                             103892 */
   /************************************************************************/
   if ( (ulOpenFlag  & 0xFFFFFFEC) ||                    /*         103892 */
        ((ulOpenFlag & 0x00000003) == 0x00000003) )      /*         103892 */
      return (ERROR_INVALID_PARAMETER);                  /*         103892 */

   /************************************************************************/
   /* Bits 16-31 are reserved, bits 10-11 and 2 must be 0.          103892 */
   /* Invalid Values for bits 4-6 are 5 and 7                       103892 */
   /* Invalid Value  for bits 0-3 are 3                             103892 */
   /************************************************************************/
   if (   (ulOpenMode &  0xFFFF0C0C)  ||                 /*         103892 */
        ( (ulOpenMode &  0x00000070) >  0x00000040 ) ||  /*         103892 */
        ( (ulOpenFlag &  0x00000003) == 0x00000003 ) )   /*         103892 */
      return (ERROR_INVALID_PARAMETER);                  /*         103892 */

   pTCB = pTCBCur;

   /*----------------------------------------------------------------
   -- set TCBSec_NOCHECK flags in tcbvar TCBCSecFlags
   -- to bypass file sharing settings in MFT_Enter
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags |= TCBSEC_NOCHECK | TCBSEC_NOCALLOUT;

   /*
    * Zero out the SFN before calling IOSftOpen since IOSftOpen only
    * modifies the low order word. If there is garbage in the high order
    * word, an invalid SFN will be returned.                        120846
    */
   if (pSFN)
     *pSFN = 0L;

   rc = IOSftOpen((vaddr_t)pszFileName,ulOpenMode,ulOpenFlag,(SFN_t*)pSFN,0);
   pTCB->TCBSecFlags &= ~(TCBSEC_NOCHECK | TCBSEC_NOCALLOUT);
   return rc;
}

/*
 ** SecPathFromSFN -- Return linear pointer to pathname\filename given an SFN
 **                   or returns null if SFN not valid.
*/
ULONG _syscall SecPathFromSFN( ULONG SFN )
{
   tcb_t     *pTCB;
   sf_entry  *pSFT;
   MFT_Entry *pMFT;
   ULONG     pMFTName = 0L;                   // Initialize.

   pTCB = pTCBCur;
   pTCB->TCBSecFlags |= TCBSEC_NOCALLOUT;     // SHUT-off callouts

   pSFT = (sf_entry *) SFTFromSFN(SFN);

   pTCB->TCBSecFlags &= ~TCBSEC_NOCALLOUT;    // Turn On callouts

   if (-1L != (ULONG) pSFT)
   {
      // pSFT is now a linear address to the SFT. Pluck the MFT pointer
      // from the SFT, then pluck the ptr to the pathname from the MFT.
      if (-1L != (ULONG)( pMFT = (MFT_Entry *) pSFT->sf_MFT ))
         pMFTName = (ULONG) &(pMFT->mft_name);
   }

   return( pMFTName );
}

/*
 ** SecSFFromSFN -- Return linear pointer to SFT
*/
ULONG _syscall SecSFFromSFN( ULONG SFN)
{
   tcb_t    *pTCB;
   ULONG rc;

   pTCB = pTCBCur;
   pTCB->TCBSecFlags |= TCBSEC_NOCALLOUT;     // SHUT-off callouts

   rc = SFTFromSFN(SFN);

   pTCB->TCBSecFlags &= ~TCBSEC_NOCALLOUT;    // Turn On callouts
   return(rc);
}

/*
 ** SecWrite   - Security Write
 *
 *  Write to a file with an optional filepos
 *
 *  Notes:
 *    p16Addr is optional,  if you have a 16:16 address for the buffer
 *            you want to pass in then do so else pass in zero
 *    Offset  is the absolute position in file to write.  To write at
 *            the current position pass in -1 (0xFFFFFFFF).
 *
*/
ULONG _syscall SecWrite( ULONG SFN,        // System File Number
               PULONG pcbBytes,   // number of bytes to write
               PUCHAR pBuffer,    //  0:32 Address
                ULONG p16Addr,    // 16:16 Address
                ULONG Offset      //  Absolute offset in file to write
              )
{
   ULONG    rc;
   tcb_t    *pTCB;


   pTCB = pTCBCur;


   /*----------------------------------------------------------------
   -- set flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags |= (TCBSEC_NOCALLOUT | TCBSEC_NOLOCKSFT);

   rc =IOSftWriteAt(SFN,(xfercnt_t* )pcbBytes,pBuffer,(vaddr_t)p16Addr,Offset);


   /*----------------------------------------------------------------
   -- restore flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags &= ~(TCBSEC_NOCALLOUT | TCBSEC_NOLOCKSFT);


   return rc;
}

/*
 * SecRead  -- allow security subsystem to read a file
 *  Notes:
 *    p16Addr is optional,  if you have a 16:16 address for the buffer
 *            you want to pass in then do so else pass in zero
 *    Offset  is the absolute position in file to read.  To read
 *            the current position pass in -1 (0xFFFFFFFF).
*/
ULONG _syscall SecRead ( ULONG SFN,        // System File Number
               PULONG pcbBytes,   // number of bytes to read
               PUCHAR pBuffer,    //  0:32 Address
                ULONG p16Addr,    // 16:16 Address
                ULONG Offset      //  Absolute offset in file to write
              )
{
   ULONG    rc;
   tcb_t    *pTCB;


   pTCB = pTCBCur;

   /*----------------------------------------------------------------
   -- set flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags |= (TCBSEC_NOCALLOUT |
                         TCBSEC_NOCHECK   |
                         TCBSEC_NOLOCKSFT);


   rc = (unsigned short)IOSftReadAt ( SFN,(xfercnt_t* ) pcbBytes,pBuffer,
                                               p16Addr,  Offset);


   /*----------------------------------------------------------------
   -- restore flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags &= ~(TCBSEC_NOCALLOUT |
                          TCBSEC_NOCHECK   |
                          TCBSEC_NOLOCKSFT);


   return rc;
}
/*
 ** SecClose  -- Close a file
 *
 *
*/
ULONG _syscall SecClose (ULONG SFN)
{
   ULONG rc;
   tcb_t    *pTCB;

   pTCB = pTCBCur;
   pTCB->TCBSecFlags |= TCBSEC_NOCALLOUT;     // SHUT-off callouts
   rc= IOSftClose(SFN);
   pTCB->TCBSecFlags &= ~TCBSEC_NOCALLOUT;    // Turn On callouts
   return rc;
}

/*
 * SecQFileSize  -- Query the size of a file from SFN
*/
ULONG _syscall SecQFileSize(ULONG SFN,
                            PULONG pSize)
{
   ULONG     rc;
   tcb_t     *pTCB;



   pTCB = pTCBCur;

   /*----------------------------------------------------------------
   -- set flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags |= (TCBSEC_NOCALLOUT |
                         TCBSEC_NOLOCKSFT);


   rc = SftFileSize(SFN, pSize);

   /*----------------------------------------------------------------
   -- restore flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags &= ~(TCBSEC_NOCALLOUT |
                          TCBSEC_NOLOCKSFT);


   return(rc);
}

/*
 ** SecChgFilePtr -- perform a seek on the file
*/
ULONG _syscall SecChgFilePtr( ULONG SFN, LONG Offset, LONG Type, PULONG pAbsolute)
{
   ULONG     rc;
   tcb_t     *pTCB;



   pTCB = pTCBCur;

   /*----------------------------------------------------------------
   -- set flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags |= (TCBSEC_NOCALLOUT |
                         TCBSEC_NOLOCKSFT);


   rc = ChgFilePtr(SFN,Offset,Type,pAbsolute);


   /*----------------------------------------------------------------
   -- restore flags in tcbvar TCBCSecFlags
   ------------------------------------------------------------------*/
   pTCB->TCBSecFlags &= ~(TCBSEC_NOCALLOUT |
                          TCBSEC_NOLOCKSFT);


   return(rc);
}

/*
 **  EndLock32
 *
 *   Dummy routine to mark end of functions that can be called at swap time
*/
void EndLock32 ( void )
{
   return;
}
/* #### END  OF LOCKED CODE !!!  ########################################### */
/* ######################################################################### */


/*
 ** Sec32OpenPre ( ULONG Flags, ULONG Mode)
 *
 *  Allows security Subsystem to verity all file open requests
 *
 *  Entry:
 *    [WFP_START]   - SS based TCB variable with offset to filename
 *    [TCB_ThisSFT] - SFT this file will get assigned
 *
 *  Returns:
 *    Non-zero - Error, stop the request
 *    Zero     - Request is OK
 *
*/
ULONG Sec32OpenPre ( ULONG Flags,    // Open Flags from DosOpen
                     ULONG Mode      // Open Mode from DosOpen
                   )
{
   pSecurityImport2  ptempSecIm;
   PSZ              pName;
   USHORT           off;     // Offset of WFP_Start from TASKAREA (Stack)
   ULONG            rc = 0;
   BYTE             TCBVars[TCBIOCopySize];
   PTDAVARS         PTDAVars;
   ULONG            SFN;

   ASSERT_NOSWAP;

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));                  // 72417 now obsolete
   }


   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);

   // The IOSFtRead/Write routines need to have a valid SFT to
   // operate on.. set the ref_count so that they won't choke
   // 100943  Save current ref count, don't touch unless it IS zero...

   // This appears to be a bad Idea on an OpenPre.  The Sft has
   // not had the firFileClus and other fields filled in yet.
   // if the refcnt was 1 shsu assumed it was valid and propagated
   // the contents to other sfts for the same file.  So the sft
   // is not valid yet, so don't mark it as so. If the Sec subsys
   // needs to read/write the file they will have to do a secopen
   // on it at this point.    114176                  SDD

   //ref_count = pSFT->sf_ref_count;                            //100943
   //if (pSFT->sf_ref_count == 0)                               //100943
   //    pSFT->sf_ref_count = 1;

   SFN = GetCurSFN();

   off = pTCBCur->TCBWFP_Start;  // 72417 moved from PTDA to TCB
   pName = SSToDS(off);          // pName points to Well Formed Path name

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.OPEN_PRE != NULL) {
         rc = (*ptempSecIm->SecImp.OPEN_PRE)(pName,Flags,Mode,SFN);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   // Reset the ref_count to 0.. it will be set to one
   // later on in w_opencreate
   // 100943 pSFT->sf_ref_count = 0;
   // 117647 pSFT->sf_ref_count = ref_count;                     //100943

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));     // 72417 now obsolete
   }

   return rc;
}

/*
 **  Sec32OpenPost ( ULONG SFN, ULONG RC, ULONG Action)
 *
 *   Notifies security subsytem what happened to the open call.
 *
 *    Entry:
 *       [TCBThisSFT]
 *
 *   Returns: Return code to return to user
 *
*/
ULONG Sec32OpenPost ( ULONG  RC,     // return code user is going to get
                     ULONG Action,  // File_Created, File_Truncated, File_Existed etc.
                     ULONG Mode,
                     ULONG Flags
                   )
{
   pSecurityImport2  ptempSecIm;
   ULONG            SFN;                    // SFN of file
   PSZ              pName;                  // pointer to name
   ULONG            OurRC = 0;              // return code from this module
   //BYTE           TCBVars[TCBIOCopySize]; // storage buffer
   PBYTE            pTCBVars;
   WORD             off;                    // offset of filename in TASKAREA
   ushort_t         ref_count;              //100943
   sf_entry         *pSFT;
   tcb_t            *pTCB;
   ULONG            rc;

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);

   if (fReentrant) {
      // Since DosOpen is such a stack hog allocated save area off heap
      if (( rc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                            TCBIOCopySize,
                            (VMHOB)FSBUFOWNER,
                            0,
                            SSToDS(&pTCBVars) ) ) != 0 )
      {  // If we can't make a callout then fail the access
         return ERROR_ACCESS_DENIED;
      }
      SaveTCB(pTCBVars);
   }

   //if (fReentrant) {
   //   SaveTCB(SSToDS(TCBVars));
   //}

   off   = pTCBCur->TCBWFP_Start;// 72417 moved from PTDA to TCB
   pName = SSToDS(off);          // pName points to Well Formed Path name
   pTCB  = pTCBCur;
   pSFT  = (sf_entry*) SELVirtToLin((USHORT)(pTCB->TCBThisSFT),
                                    (SEL)((pTCB->TCBThisSFT) >> 16));

   // The IOSFtRead/Write routines need to have a valid SFT to
   // operate on.. set the ref_count so that they won't choke
   // 100943  Save current ref count, don't touch unless it IS zero...

   ref_count = pSFT->sf_ref_count;                            //100943
   if (pSFT->sf_ref_count == 0)                               //100943
       pSFT->sf_ref_count = 1;

   SFN = ExtractSFN(pSFT);


   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.
   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (OurRC == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.OPEN_POST != NULL) {
         OurRC = (*ptempSecIm->SecImp.OPEN_POST)(pName,Flags,Mode,SFN, Action,RC);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   // Reset the ref_count to 0.. it will be set later on in w_opencreate
   // 100943 pSFT->sf_ref_count = 0;

   pSFT->sf_ref_count = ref_count;                            //100943

   //if (fReentrant) {
   //    RestoreTCB(SSToDS(TCBVars));
   //}
   if (fReentrant) {
      RestoreTCB(pTCBVars);
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pTCBVars);

   }

   return(OurRC);
}


/*
 ** Sec32DeletePre -- Validate DosDelete "pre" request
 *
 *  Call Security subsystem and let them validate DosDelete request
 *
 *  Entry
 *    pszFile  -- fully qualified path
 *
 *  Exit
 *    AX = ERROR_ACCESS_DENIED if call shouldn't proceed
 *
*/
ULONG Sec32DeletePre ( PSZ pszPath )
{
   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;
   ULONG    rc = 0;

   ASSERT_NOSWAP;

   if (fReentrant) {
      // Save any PTDA and TCB Variables that FSD_Delete depends on
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));     // 72417 now obsolete
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.DELETE_PRE != 0 ) {
         rc = (*ptempSecIm->SecImp.DELETE_PRE)(pszPath);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));     // 72417 now obsolete
   }

   return rc;
}

/*
 ** Sec32DeletePost -- Validate DosDelete "post" request
 *
 *  Call Security subsystem and let them validate DosDelete request
 *
 *  Entry
 *    pszPath  -- fully qualified path
 *    PostRC   -- return code from attempted deletion of the file
 *
 *  Exit
 *
*/
VOID Sec32DeletePost ( PSZ pszPath, ULONG PostRC )
{
   pSecurityImport2 ptempSecIm;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while (ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.DELETE_POST != NULL ) {
         (*ptempSecIm->SecImp.DELETE_POST)(pszPath, PostRC);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   return;
}

/*
 ** Sec32MovePre  -- Validate DosMove "pre" request
 *
 *  Call Security subsytem and let them validate DosMove request
 *
 *  Entry
 *     pszNew     -- New File Name
 *     pszOld     -- Old File Name
 *
 *  Exit
 *    AX = ERROR_ACCESS_DENIED if call shouldn't proceed
 *
*/
ULONG Sec32MovePre (PSZ pszNew,   // Destination path
                    PSZ pszOld)   // Old path
{
   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;
   ushort_t Ren_WFP;
   ushort_t WFP_Path_End;
   ULONG    rc = 0;

   ASSERT_NOSWAP;

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
      // Save any PTDA and TCB Variables that FSD_Move depends on
//    SavePTDA(SSToDS(&PTDAVars));           //72417 now obsolete
      Ren_WFP         = pTCBCur->TCBRen_WFP;       // 72417 moved from PTDA to TCB
      WFP_Path_End    = pTCBCur->TCBWFP_Path_End;  // 72417 moved from PTDA to TCB
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.MOVE_PRE != 0) {
         rc = (*ptempSecIm->SecImp.MOVE_PRE)(pszNew, pszOld);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));           // 72417 now obsolete
      // Restore mostly transpath related TCB vars
      pTCBCur->TCBWFP_Path_End = WFP_Path_End;  // 72417 moved from PTDA to TCB
      pTCBCur->TCBRen_WFP      = Ren_WFP;       // 72417 moved from PTDA to TCB

   }
   return rc;
}

/*
 ** Sec32MovePost  -- Validate DosMove "post" request
 *
 *  Call Security subsytem and let them validate DosMove request
 *
 *  Entry
 *     pszNew     -- New File Name
 *     pszOld     -- Old File Name
 *     PostRC     -- return code from move attempt
 *
 *  Exit
 *
*/
VOID Sec32MovePost (PSZ   pszNew,   // Destination path
                    PSZ   pszOld,   // Old path
                    ULONG PostRC)   // Return code from move attempt
{
   pSecurityImport2 ptempSecIm;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while (ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.MOVE_POST != 0) {
         (*ptempSecIm->SecImp.MOVE_POST)(pszNew, pszOld, PostRC);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   return;
}

/*
 ** Sec32GetModule -- verification of loader open
 *
 *  Called whenever a process tries to load a dll or exe into its
 *  address space.
 *
*/
ULONG Sec32GetModule ( PSZ pszModName)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG) ptempSecIm->SecImp.GETMODULE != NULL) {
         rc = (*ptempSecIm->SecImp.GETMODULE)((PSZ)pszModName);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While
   return rc;
}



/*
 ** Sec32Close
 *
 *  Notification of a DosClose
 *
 *  Entry:
 *    [TCBThisSFT]
 *    Handle  -> Handle user provided
 *
*/
void Sec32Close (ULONG Handle )
{

   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return;

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
      // No ptda variables seem to be in danger here
   }

   //  Check with each registered subsystem.
   ptempSecIm = pSecIm_list_head;
   while  ( ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.CLOSE != NULL) {
         (*ptempSecIm->SecImp.CLOSE)(GetCurSFN());
      }

      Handle;              //???

      ptempSecIm = ptempSecIm->pNextSecIm;
   }

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
   }
}



/*
 ** SecCreateVDM  -- Notification of the creation of a a VDM
 *
 *  Call Security subsystem and let them give a yes/no
 *
 *  Entry
 *     pszProg       - Non qualified filename to be executed
 *     pszArgs       - Args to be passed to program
 *
*/
ULONG SecCreateVDM ( PSZ pszProg, PSZ pszArgs)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;

   ASSERT_NOSWAP;

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.CREATEVDM) {
         rc = (*ptempSecIm->SecImp.CREATEVDM)(pszProg,pszArgs);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;
   }
   return(rc);
}
/*
 ** SecCreateVDMPost  -- Post Notification of the creation of a a VDM
 *
 *  Inform security system of return code from VDM Create
 *
 *  Entry
 *    rc = Return code from VDM creation
 *
*/
void SecCreateVDMPost ( int vdmrc)
{
   pSecurityImport2 ptempSecIm;

   ASSERT_NOSWAP;

   ptempSecIm = pSecIm_list_head;
   while (ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.CREATEVDMPOST) {
         (*ptempSecIm->SecImp.CREATEVDMPOST)(vdmrc);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;
   }
   return;
}


/*
 **  SecLdrOpen
 *
 *   Validation of the loader opening a file
 *
 *   We call the Security Subsystem to allow them to validate the
 *   open by the loader.  If they fail the call then we must take
 *   action to close the file as it has already been opened.
 *
 *   EXIT:
 *     RC= 0 Let the call proceed
 *     RC!=0 Fail the call
*/
ULONG SecLdrOpen ( PSZ pszFile, PULONG pSFN)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;
   ULONG CloseRC = 0;

   ASSERT_NOSWAP;


   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.LOADEROPEN) {
         rc = (*ptempSecIm->SecImp.LOADEROPEN)(pszFile,*pSFN);
         if (rc) {
            CloseRC = IOSftClose(*pSFN);
         }
      }
      ptempSecIm = ptempSecIm->pNextSecIm;
   }
   // What if close fails.. return 0?
   return rc;
}

/*
 ** SecExec
 *
 *  Validation of DosExecPgm
 *
 *  Let the security subsystem validate a DosExecPgm before any real
 *  work is done on the exec.
 *
*/
ULONG SecExec ( ULONG pPath,       // linear ptr to path @108011-was 16:16
                ULONG p16Args,     // 16:16 ptr to args
                char *TmpBuf)      // Temp Buf to copy path into
{
   pSecurityImport2 ptempSecIm;
   PCHAR pchArgs;
   ULONG rc = 0;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   pchArgs = (PSZ) SELVirtToLin((USHORT)p16Args,(USHORT)( p16Args >> 16));
   // This is kind of buggy but seems to work.  tkExecPgm allocates
   // a temp buffer and we need to copy the process name into it.  If
   // I do this work in tkExecPgm I have to do a VirtToLin there to
   // do a strcpy.  If I wait till here, then both of those functions
   // are performed here, saving space in the non-security case
   strcpy(TmpBuf,pPath);

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.EXECPGM) {
         rc = (*ptempSecIm->SecImp.EXECPGM)((PSZ)pPath,pchArgs);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }
   return rc;
}


/*
 ** SecExecPost
 *
 *  Post notification of DosExecPgm
 *
 *  Notification of DosExecPgm.  No chance to fail the call at this point,
 *  just notification of the child process ID.
 *
*/
VOID  SecExecPost ( ULONG pPath,    // linear ptr to path @108011-was 16:16
                    ULONG p16Args,  // 16:16 ptr to args
                    ULONG NewPID )
{
   pSecurityImport2 ptempSecIm;
   PCHAR pchArgs;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while  (ptempSecIm != NULL )  {

      if ((ULONG)ptempSecIm->SecImp.EXECPGMPOST) {
         pchArgs = (PSZ) SELVirtToLin((USHORT)p16Args,(USHORT)( p16Args >> 16));
         (*ptempSecIm->SecImp.EXECPGMPOST)((PSZ)pPath,pchArgs,NewPID);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

  }
   return;
}


/*
 ** Sec32QueryFileInfo
 *
 *  Validation of DosQueryFileInfo
 *
 *  Let the security subsystem validate and/or modify parameters for
 *  DosQueryFileInfo.  This is a post call.
 *
  */
ULONG Sec32QueryFileInfo ( ptr_t p16Buffer,     // 16:16 ptr to user buff
                           ULONG BufSize,       // size of user buffer
                           ULONG InfoLevel      // infolevel
                         )
{
   pSecurityImport2 ptempSecIm;
   ULONG    SFN;
   PBYTE    pInfo;
   ULONG    rc = 0;

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);                                    // return the default

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.QUERYFILEINFO != 0) {
          // reentrancy not a big deal here since api already completed
          SFN   = GetCurSFN();
          pInfo = (PBYTE) SELVirtToLin(p16Buffer.ptr_off, p16Buffer.ptr_sel);
          rc =  (*ptempSecIm->SecImp.QUERYFILEINFO)(SFN, pInfo, BufSize, InfoLevel);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }
   return(rc);

}


/*
 ** Sec32SetFileInfo
 *
 *  Validation of DosSetFileInfo
 *
 *  Let the security subsystem validate and/or modify parameters for
 *  DosSetFileInfo.  This is a pre call.
 *
  */
ULONG Sec32SetFileInfo ( ptr_t p16Buffer,     // 16:16 ptr to user buff
                         ULONG BufSize,       // size of user buffer
                         ULONG InfoLevel      // infolevel
                       )
{
   pSecurityImport2 ptempSecIm;
   ULONG    SFN;
   PBYTE    pInfo;
   ULONG    rc = 0;
   BYTE     TCBVars[TCBIOCopySize];

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);                                    // return the default

   if (fReentrant)
   {
     SaveTCB(SSToDS(TCBVars));
     // No ptda variables seem to be in danger here
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   SFN   = GetCurSFN();
   pInfo = (PBYTE) SELVirtToLin(p16Buffer.ptr_off, p16Buffer.ptr_sel);

   rc = ProbeIt(pInfo, BufSize, 0);
   if (!rc ) {
      ptempSecIm = pSecIm_list_head;
      while ( (ptempSecIm != NULL ) && (rc == 0) ) {

         if ((ULONG)ptempSecIm->SecImp.SETFILEINFO != 0) {
             rc =  (*ptempSecIm->SecImp.SETFILEINFO)( SFN,
                                                      pInfo,
                                                      BufSize,
                                                      InfoLevel);
         }
         ptempSecIm = ptempSecIm->pNextSecIm;
      }
   }

   if (fReentrant)
   {
      RestoreTCB(SSToDS(TCBVars));
   }

   return(rc);

}

/*
 ** Sec32SetFileMode
 *
 *  Validation of DosSetFileMode
 *
 *  Let the security subsystem validate and/or modify parameters for
 *  DosSetFileMode.  This is a pre call.
 *
  */
ULONG Sec32SetFileMode ( PSZ   pszPath,       // ptr to pathname
                         ptr_t p16NewAttrib   // 16:16 ptr to new attributes
                       )
{
   pSecurityImport2 ptempSecIm;
   PUSHORT  pNewAttribute;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;
   ULONG    rc = 0;

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);                                    // return the default

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));     //72417 now obsolete
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   pNewAttribute = (PUSHORT) SELVirtToLin( p16NewAttrib.ptr_off,
                                           p16NewAttrib.ptr_sel );

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.SETFILEMODE != 0) {
          rc =  (*ptempSecIm->SecImp.SETFILEMODE)( pszPath,
                                                   pNewAttribute );
      }
      ptempSecIm = ptempSecIm->pNextSecIm;
   }

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));        // 72417 now obsolete
   }

   return(rc);
}


/*
 ** Sec32SetPathInfo
 *
 *  Validation of DosSetPathInfo
 *
 *  Let the security subsystem validate and/or modify parameters for
 *  DosSetPathInfo.  This is a pre call.
 *
  */
ULONG Sec32SetPathInfo ( PSZ   pPathName,     // well formed pathname
                         ULONG InfoLevel,     // info level
                         ptr_t p16Buffer,     // 16:16 ptr to user buff
                         ULONG BufSize,       // size of user buffer
                         ULONG InfoFlags      // info flags
                       )
{
   pSecurityImport2 ptempSecIm;
   PBYTE    pInfo;
   ULONG    rc = 0;
   BYTE     TCBVars[TCBIOCopySize];
   ushort_t WFP_Start;
   ushort_t Curr_Dir_End;
   char     PathNameType;

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);                                    // return the default

   if (fReentrant)
   {
     SaveTCB(SSToDS(TCBVars));
      // Save any PTDA and TCB Variables that FSD_SetPathInfo depends on
      WFP_Start       = pTCBCur->TCBWFP_Start;      // 72417 moved from PTDA to TCB
      Curr_Dir_End    = pTCBCur->TCBCurr_Dir_End;   // 72417 moved from PTDA to TCB
      PathNameType    = pTCBCur->TCBPathNameType;   // 72417 moved from PTDA to TCB
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   pInfo = (PBYTE) SELVirtToLin(p16Buffer.ptr_off, p16Buffer.ptr_sel);

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.SETFILEINFO != 0) {
          rc =  (*ptempSecIm->SecImp.SETPATHINFO)( pPathName,
                                                   InfoLevel,
                                                   pInfo,
                                                   BufSize,
                                                   InfoFlags);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;
   }

   if (fReentrant)
   {
      RestoreTCB(SSToDS(TCBVars));
      // Restore PTDA variables.
      pTCBCur->TCBWFP_Start       = WFP_Start;      // 72417 moved from PTDA to TCB
      pTCBCur->TCBCurr_Dir_End    = Curr_Dir_End;   // 72417 moved from PTDA to TCB
      pTCBCur->TCBPathNameType    = PathNameType;   // 72417 moved from PTDA to TCB
   }

   return(rc);
}


/*
 ** Sec32FindFirst  --
 *
 *  Allows security subsystem to screen a DosFindFirst  call
 *
 *
 *  Entry
 *     [WFP_START] -> Ptr to start of Well formed path
 *     [TCBfSharing] == -1 if this call uses position info
 *
 *  Exit
 *    CX = Result Count
 *    AX = ERROR if call shouldn't proceed
*/
ULONG Sec32FindFirst (ULONG  ulSearchHandle,          // Search Handle
                      ULONG  RC,                      // return code
                      PULONG pResultCntInfoLevel,     // Result Count:Level
                      ULONG  ResultBuf,               // Result Buffer 16:16
                      ULONG  BufSize,                 // Result Buffer size
                      ULONG  ulRequested              // Number entries requested
                      )
{

   pSecurityImport2 ptempSecIm;
   ULONG   rc = 0;
   FINDPARMS FindParms;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;

   ASSERT_NOSWAP;

   // this needs to be before the check for no callouts since
   // the user will get the resultcntinfolevel resultcnt back
   // even if we return early so we need to get it straightened
   // out.
   FindParms.usLevel    = *(PUSHORT)  pResultCntInfoLevel;
   *pResultCntInfoLevel = (*pResultCntInfoLevel)>>16; // strip Infolevel out

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(RC);                                   // return the default

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));     //72417 now obsolete
   }


   // All the filesystems buffers sizes are limited to 16 bits.  Parameters
   // are passed into this routine as longs, but they will never exceed
   FindParms.pszPath  = SSToDS(pTCBCur->TCBWFP_Start);   // 72417 moved from PTDA to TCB
   FindParms.ulHandle = ulSearchHandle;
   FindParms.rc       = RC;
   FindParms.pResultCnt = (PUSHORT) pResultCntInfoLevel;
   FindParms.usReqCnt = (USHORT) ulRequested;
   FindParms.usBufSize =(USHORT) BufSize;
   FindParms.pcBuffer = (PCHAR)SELVirtToLin((USHORT)ResultBuf,
                                     (USHORT)(ResultBuf >>16));


   // if TCBfSharing == -1 then fPosition = 1 else fPosition = 0
   FindParms.fPosition = (USHORT)((pTCBCur->TCBfSharing == -1) ? 1 : 0);

   FindParms.Position    = 0;
   FindParms.pszPosition = 0;


   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.FINDFIRST  != 0) {
         rc = (*ptempSecIm->SecImp.FINDFIRST)(SSToDS(&FindParms));
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   if (fReentrant) {
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));        // 72417 now obsolete
   }
   return rc;
}



/*
 ** Sec32FindFirst3x
 *
 *  Call Security subsystem and let them validate FindFirst3x request
 *
 *  Entry
 *    pszPath  -- fully qualified path with metacharacters
 *
 *  Exit
 *    AX = ERROR_ACCESS_DENIED if call shouldn't proceed
 *
*/
ULONG Sec32FindFirst3x ( ULONG ulSrchHandle, PSZ pszPath )
{
   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;


   ULONG    rc = 0;

   ASSERT_NOSWAP;

   if (fReentrant) {
      // Save any PTDA and TCB Variables that FSD_Delete depends on
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));           // 72417 now obsolete
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.FINDFIRST3X != 0 ) {
         rc = (*ptempSecIm->SecImp.FINDFIRST3X)(ulSrchHandle, pszPath); //DGE02
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));           // 72417 now obsolete
   }

   return rc;
}

/*
 ** Sec32FindFirstNext3x
 *
 *  Call Security subsystem and validate FindFirst/Next3x request
 *
 *  Entry
 *     [ptda_pSrchBuf]  - Address of SearchBuffer
 *
 *  Exit
 *    AX = ERROR_ACCESS_DENIED if call shouldn't proceed
 *
*/
ULONG Sec32FindFirstNext3x ( ULONG ulSrchHandle )
{
   pSecurityImport2 ptempSecIm;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;

   ULONG    rc = 0;
   PSZ      pFile;

   ASSERT_NOSWAP;

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)     // callouts shut off
      return(0);                                    // return the default

   if (fReentrant) {
      // Save any PTDA and TCB Variables that FSD_Delete depends on
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));                  // 72417 now obsolete
   }
   pFile = SSToDS(pTCBCur->TCB_pSrchBuf);           // 72417 moved from PTDA to TCB
   pFile += sizeof(struct find_buf_cominfo);

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.FINDFIRSTNEXT3X != 0 ) {
        rc = (*ptempSecIm->SecImp.FINDFIRSTNEXT3X)(ulSrchHandle,pFile);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));        // 72417 now obsolete
   }

   return rc;
}

/*
 ** Sec32FindClose3x
 *
 *  Call Security subsystem and validate FindClose3x request
 *
 *  Entry
 *
 *  Exit
 *
*/
ULONG Sec32FindClose3x ( ULONG ulSrchHandle )
{
   ULONG    rc = 0;
   pSecurityImport2 ptempSecIm;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.FINDCLOSE3X != 0 ) {
         rc = (*ptempSecIm->SecImp.FINDCLOSE3X)( ulSrchHandle );
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   }

   return rc;
}

/*
 ** Sec32FindNext  --
 *
 *  Allows security subsystem to fail a DosFindNext  call
 *
 *
 *  Entry
 *     [TCBfSharing] == -1 if this call uses position info
 *
 *
 *
 *  Exit
 *    CX = Result Count
 *    AX = ERROR if call shouldn't proceed
*/
ULONG Sec32FindNext ( ULONG  ulSearchHandle,  //Search Handle
                      ULONG  RC,              // return code for user
                      PULONG pResultCntInfoLevel, // Result Count:Level
                      ULONG ResultBuf,         // Result Buffer 16:16
                      ULONG BufSize,           // Result Buffer size
                      ULONG RequestedCnt       // number of dir entries user requested
                      )
{

   pSecurityImport2 ptempSecIm;
   ULONG   rc = 0;
   FINDPARMS FindParms;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;

   ASSERT_NOSWAP;

   // this needs to be before the check for no callouts since
   // the user will get the resultcntinfolevel resultcnt back
   // even if we return early so we need to get it straightened
   // out.
   FindParms.usLevel    = *(PUSHORT)  pResultCntInfoLevel;
   *pResultCntInfoLevel = (*pResultCntInfoLevel)>>16; // strip Infolevel out

   if (pTCBCur->TCBSecFlags & TCBSEC_NOCALLOUT)  {   // callouts shut off
      // pResultCntInfoLevel has been fixed already
      return(RC);                                   // return the default rc
   }

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));                  // 72417 now obsolete
   }

   FindParms.pszPath    = 0;
   FindParms.ulHandle   = ulSearchHandle;
   FindParms.rc         = RC;
   FindParms.pResultCnt = (PUSHORT)   pResultCntInfoLevel;
   FindParms.usReqCnt   = (USHORT)    RequestedCnt;
   FindParms.usBufSize  = (USHORT)  BufSize;
   FindParms.pcBuffer   = (PCHAR)SELVirtToLin((USHORT)ResultBuf,
                                                   (USHORT)(ResultBuf >>16));

   // if TCBfSharing == -1 then fPosition = 1 else fPosition = 0
   FindParms.fPosition = (USHORT) ((pTCBCur->TCBfSharing == -1) ? 1 : 0);
   FindParms.Position    = 0;
   FindParms.pszPosition = 0;


   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG)ptempSecIm->SecImp.FINDNEXT  != 0) {
         rc = (*ptempSecIm->SecImp.FINDNEXT)( SSToDS(&FindParms));
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));              // 72417 now obsolete
   }
   return rc;
}

/*
 ** Sec32FindClose  --
 *
 *  Allows security subsystem to know when a search is finished
 *
 *  Entry
 *
 *  Exit
*/
VOID Sec32FindClose( ULONG  ulSearchHandle   //Search Handle             DGE02
                      )
{

   pSecurityImport2 ptempSecIm;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.

   ptempSecIm = pSecIm_list_head;
   while ( ptempSecIm != NULL ) {

      if ((ULONG)ptempSecIm->SecImp.FINDCLOSE  != 0) {
         (*ptempSecIm->SecImp.FINDCLOSE)(ulSearchHandle);              //DGE02
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While
   return;
}


/*
 ** Sec32MakeDir --
 *
 *  Called when a process tries to create a directory
 *
 *
*/
ULONG Sec32MakeDir ( PSZ pszDirName)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;

   ASSERT_NOSWAP;

   if (fReentrant) {
      // Save any PTDA and TCB Variables that FSD_Delete depends on
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));           // 72417 now obsolete
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG) ptempSecIm->SecImp.MAKEDIR != NULL) {
         rc = (*ptempSecIm->SecImp.MAKEDIR)((PSZ)pszDirName);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));        // 72417 now obsolete
   }

   return rc;
}


/*
 ** Sec32ChangeDir --
 *
 *  Called when a process tries to change directory
 *
 *
*/
ULONG Sec32ChangeDir ( PSZ pszDirName)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;

   ASSERT_NOSWAP;

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));              // 72417 now obsolete
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG) ptempSecIm->SecImp.CHANGEDIR != NULL) {
         rc = (*ptempSecIm->SecImp.CHANGEDIR)((PSZ)pszDirName);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));              // 72417 now obsolete
   }
   return rc;
}

/*
 ** Sec32RemoveDir --
 *
 *  Called when a process tries to remove a directory
 *
 *
*/
ULONG Sec32RemoveDir ( PSZ pszDirName)
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;
   BYTE     TCBVars[TCBIOCopySize];
   PTDAVARS PTDAVars;

   ASSERT_NOSWAP;

   if (fReentrant) {
      SaveTCB(SSToDS(TCBVars));
//    SavePTDA(SSToDS(&PTDAVars));           // 72417 now obsolete
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG) ptempSecIm->SecImp.REMOVEDIR != NULL) {
         rc = (*ptempSecIm->SecImp.REMOVEDIR)((PSZ)pszDirName);
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   if (fReentrant) {
      // Restore PTDA and TCB variables
      RestoreTCB(SSToDS(TCBVars));
//    RestorePTDA(SSToDS(&PTDAVars));              // 72417 now obsolete
   }
   return rc;
}

/*
 ** Sec32SetDateTime --
 *
 *  Called when a process tries to change the date/time.
 *
*/
ULONG Sec32SetDateTime ( PDATETIME pDateTimeBuf )
{
   pSecurityImport2 ptempSecIm;
   ULONG rc = 0;

   ASSERT_NOSWAP;

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) ) {

      if ((ULONG) ptempSecIm->SecImp.SETDATETIME != NULL) {
         rc = (*ptempSecIm->SecImp.SETDATETIME)( pDateTimeBuf );
      }
      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While
   return rc;
}

/*
 ** Sec32DevIoctl --
 *
 *  Category 8 and 9 DosDevIOCtl "pre" callout.
 *
 *  Note that we won't use the length fields that are passed in, but
 *  instead, we'll calculate the necessary lengths. We need to do this
 *  since 0 is a valid length.
*/
ULONG Sec32DevIoctl ( ULONG  SFN,
                      ULONG  Category,
                      ULONG  Function,
                      ptr_t  p16ParmList,
                      ULONG  cbParmList,
                      ptr_t  p16DataArea,
                      ULONG  cbDataArea,
                      ULONG  DiskNumber )
{
   pSecurityImport2 ptempSecIm;
   PUCHAR pParmList;
   PUCHAR pDataArea;
   ULONG  rc           = 0;
   BOOL   bProbeParmRW = FALSE;
   BOOL   bProbeDataRW = TRUE;
   BOOL   bProbeParm;
   BOOL   bProbeData;
   ULONG  ulParmLen;
   ULONG  ulDataLen;
   BOOL   bCallout   = TRUE;      // Assume we will do the callout.
   USHORT NumSectors;
   USHORT i;
   PDDI_FormatPacket_param pFP;
   PDDI_RWVPacket_param    pRWVP;

   cbParmList, cbDataArea;        // Avoid compiler warning.

   ASSERT_NOSWAP;

   /*
    *  Note that we can get a ParmList or DataArea address of 0, even
    *  though there is a required/reserved parameter or data. The workplace
    *  shell passes 0 sometimes.
    *  If SelVirtToLin fails and the ParmList or DataArea that it
    *  points to is only to point to something like a reserved byte of zero,
    *  we will still invoke the callout but will pass a pointer of NULL
    *  and a length of 0. The ISS should then not reference this.
    *  If we really have an invalid address and we don't invoke the callout,
    *  the call will be failed later.
    */
   pParmList = (PUCHAR) SELVirtToLin(p16ParmList.ptr_off,p16ParmList.ptr_sel);

   if (-1L == (ULONG) pParmList)
   {
      pParmList = NULL;
      bProbeParm = FALSE;
      ulParmLen = 0L;
   }
   else
   {
      bProbeParm = TRUE;
      ulParmLen = 1L;                     // Default length.
   }

   pDataArea = (PUCHAR) SELVirtToLin(p16DataArea.ptr_off,p16DataArea.ptr_sel);

   if (-1L == (ULONG) pDataArea)
   {
      pDataArea = NULL;
      bProbeData = FALSE;
      ulDataLen = 0L;
   }
   else
   {
      bProbeData = TRUE;
      ulDataLen = 1L;                     // Default length.
   }

   //  Check with each registered subsystem.
   //  If someone returns NonZero, remaining subsystems won't be called.

   ptempSecIm = pSecIm_list_head;
   while ( (ptempSecIm != NULL ) && (rc == 0) )
   {

      if ((ULONG) ptempSecIm->SecImp.DEVIOCTL != NULL)
      {
            switch (Function)
            {
               case IODC_LK:             // 00-Lock drive   (both cat 8 & 9)
               case IODC_UL:             // 01-Unlock drive (both cat 8 & 9)
               case IODC_RM:             // 02-Redetermine media   (cat 8)

                 // Parms and Data are same for all the above (all zeros).
                 bProbeDataRW = FALSE;      // Data is read-only.
                 break;

               case IODC_SL:             // 03-Set Logical Map     (cat 8)

                 if (NULL == pDataArea)
                    bCallout = FALSE;       // Good address is required.
                 break;

               case IODC_BF:             // 04-Begin Format        (cat 8)

                 if (NULL == pParmList)
                 {
                    bCallout = FALSE;       // Good address is required.
                    break;
                 }

                 // Use TKFuBuffLenz to find length of string.
                 // A strlen of 0 means it is for FAT (not an FSD name).

                 if ( rc = TKFuBuffLenz( SSToDS(&ulParmLen), // Returned length
                                         pParmList,    // User buffer.
                                         CCHMAXPATH,   // Max FSD name length
                                         0 ))          // Error not fatal.
                    bCallout = FALSE;
                 else
                 {
                    bProbeParm = FALSE;       // We're done probing parms.
                    bProbeDataRW = FALSE;     // Data is read-only.
                 }
                 break;

               case IODC_BR:             // 20-Block Removable     (cat 8)

                 if (pParmList)
                 {
                   if (-1L == SFN)           // No previous file handle
                     ulParmLen = 2L;         //   so add byte for drive unit.
                 }

                 if (NULL == pDataArea)
                    bCallout = FALSE;       // Good address is required.
                 break;

               case IODC_GL:             // 21-Get Logical Map     (cat 8)

                 if (NULL == pDataArea)
                    bCallout = FALSE;       // Good address is required.
                 break;

#ifdef INTERNALIOCTL
               case IOPD_ED:             // 22-Install BDS funcs   (cat 9)
                 ulParmLen = sizeof(PDDI_DriveAlias_param);???
                 bProbeData = FALSE;         // No meaningful data.
                 break;
#endif

               case IODC_RC:             // 40-Removable Media Ctl (cat 8)

                 if (NULL == pParmList)
                 {
                    bCallout = FALSE;       // Good address required.
                    break;
                 }
                 else
                 {
                   if (-1L == SFN)           // No previous file handle
                     ulParmLen = 2L;         //   so add byte for drive unit.

                   pDataArea = NULL;         // No meaningful data.
                   bProbeData = FALSE;
                   ulDataLen = 0L;
                 }
                 break;

               case IODC_SP:             // 43-Set Device Params   (cat 8)

                 if ((NULL == pParmList) || (NULL == pDataArea))
                 {
                    bCallout = FALSE;       // Good addresses required.
                    break;
                 }

                 // Validate the command byte in the parameter packet.

                 if (rc = ProbeIt( pParmList,
                                   ulParmLen,
                                   bProbeParmRW ))
                    bCallout = FALSE;
                 else
                 {
                   if ((-1L == SFN) && (COMMAND_01 == pParmList[0]))
                     ulParmLen = 2L;
                   else
                     bProbeParm = FALSE;    // We're done probing parms.

                   ulDataLen = sizeof(DDI_DeviceParameters_data);
                 }
                 break;

               case IODC_FT:             // 45-Format Track (both cat 8 & 9)

                 if ((NULL == pParmList) || (NULL == pDataArea))
                 {
                    bCallout = FALSE;       // Good addresses required.
                    break;
                 }

                 // The following algorithm to determine the size of the
                 // parameter list is taken from function CheckFormatPacket
                 // in \src\dev\dasd\os2dasd\dmioctl.c

                 pFP = (PDDI_FormatPacket_param) pParmList;
                 ulParmLen = sizeof(DDI_FormatPacket_param);

                 // Validate the parameter packet.

                 if (rc = ProbeIt( (PUCHAR) pFP,
                                   ulParmLen,
                                   bProbeParmRW ))
                 {
                    bCallout = FALSE;
                    break;
                 }

                 // The parameter packet is safe to read and at least one
                 // entry in the track table is accessible.  We must now
                 // determine if we will have to go past this entry in the
                 // track table.  If we do, ProbeIt will have to be
                 // called again with a new size.

                 if (FP_COMMAND_MULTITRACK != pFP->Command)
                    NumSectors = pFP->NumSectors;
                 else
                    NumSectors = 1;

                 if (NumSectors > 1)
                 {
                    ulParmLen = sizeof(DDI_FormatPacket_param) +
                                (sizeof(FTT) * (NumSectors - 1));

                    if (rc = ProbeIt( (PUCHAR) pFP,
                                      ulParmLen,
                                      bProbeParmRW ))
                    {
                      bCallout = FALSE;
                      break;
                    }
                 }

                 // THE DASD MANAGER DOES ADDITIONAL VALIDATION OF THE
                 // PARAMETER LIST WHICH IS NOT BEING DUPLICATED HERE.
                 // WE WILL PASS THE PACKET TO THE CALLOUT, BUT IT IS STILL
                 // POSSIBLE THAT THE DASD MANAGER MAY REJECT IT.

                 bProbeParm = FALSE;         // Done probing Parms.
                 ulDataLen = sizeof(DDI_FormatPacket_data);
                 break;

#ifdef INTERNALIOCTL
               case IOPD_DM:             // 47-Set DASD Mgr Parms (cat 8 & 9)
                 ulParmLen = sizeof(DDI_QueueControl_param);
                 bProbeData = FALSE;         // No meaningful data???
                 break;
#endif

               case IODC_QD:             // 5D-Diskette Control (cat 8)

                 if (NULL == pParmList)
                    bCallout = FALSE;       // Good address is required.
                 break;

#ifdef INTERNALIOCTL
               case IOPD_RB:             // 5E-Readback function   (cat 9)
                 ulParmLen = sizeof(DDI_ReadBack_param);
                 // Data is only meaningful for DMA_QUERY_READ_STATS
                 if (???)
                 {
                   ulDataLen = sizeof(DDI_ReadBack_data);
                 }
                 break;
               case IOPD_DO:             // 5F-DMA overrun func    (cat 9)
                 ??????
                 break;
#endif
               case IODC_MS:             // 60-Media Sense         (cat 8)

                 if (NULL == pDataArea)
                    bCallout = FALSE;       // Good address is required.
                 else
                    ulDataLen = sizeof(DDI_MediaSense_data);
                 break;

               case IODC_GP:             // 63-Get Device Params   (cat 8)

                 if ((NULL == pParmList) || (NULL == pDataArea))
                 {
                    bCallout = FALSE;       // Good addresses required.
                    break;
                 }

                 // Validate the command byte in the parameter packet.

                 if (rc = ProbeIt( pParmList,
                                   ulParmLen,
                                   bProbeParmRW ))
                    bCallout = FALSE;
                 else
                 {
                   if (Category == IOC_DC)     // Category 8
                   {
                     if ((-1L == SFN) && (COMMAND_00 == pParmList[0]))
                       ulParmLen = 2L;
                     else
                       bProbeParm = FALSE;    // We're done probing parms.

                     ulDataLen = sizeof(DDI_DeviceParameters_data);
                   }
                   else                        // Category 9
                   {
                     bProbeParm = FALSE;    // We're done probing parms.
                     ulDataLen = sizeof(DDI_PhysDeviceParameters_data);
                   }
                 }

                 break;

               case IODC_WT:             // 44-Write  Track (both cat 8 & 9)
               case IODC_RT:             // 64-Read   Track (both cat 8 & 9)
               case IODC_VT:             // 65-Verify Track (both cat 8 & 9)

                 if ((NULL == pParmList) ||
                    ((NULL == pDataArea) && (IODC_VT != Function)))
                 {
                    bCallout = FALSE;       // Good addresses required.
                    break;
                 }

                 // The following algorithm to determine the size of the
                 // parameter list is taken from function CheckRWVPacket
                 // in \src\dev\dasd\os2dasd\dmioctl.c

                 pRWVP = (PDDI_RWVPacket_param) pParmList;
                 ulParmLen = sizeof(DDI_RWVPacket_param);

                 // Validate the parameter packet.

                 if (rc = ProbeIt( (PUCHAR) pRWVP,
                                   ulParmLen,
                                   bProbeParmRW ))
                 {
                    bCallout = FALSE;
                    break;
                 }

                 // The parameter packet is safe to read and at least one
                 // entry in the track table is accessible.  We must now
                 // determine if we will have to go past this entry in the
                 // track table.  If we do, ProbeIt will have to be
                 // called again with a new size.

                 if  ( (pRWVP->FirstSector + pRWVP->NumSectors) > 1)
                 {

                    ulParmLen = sizeof(DDI_RWVPacket_param) +
                                (sizeof(TLT) *
                                (pRWVP->FirstSector + pRWVP->NumSectors - 1));

                    if (rc = ProbeIt( (PUCHAR) pRWVP,
                                      ulParmLen,
                                      bProbeParmRW ))
                    {
                      bCallout = FALSE;
                      break;
                    }
                 }

                 bProbeParm = FALSE;         // Done probing Parms.

                 // THE DASD MANAGER DOES ADDITIONAL VALIDATION OF THE
                 // PARAMETER LIST WHICH IS NOT BEING DUPLICATED HERE.
                 // WE WILL PASS THE PACKET TO THE CALLOUT, BUT IT IS STILL
                 // POSSIBLE THAT THE DASD MANAGER MAY REJECT IT.

                 // Validate the data packet if not Verify Track.

                 ulDataLen  = 0L;

                 if (IODC_VT != Function)    // Read or Write Track.
                 {
                    if (IODC_RT == Function)    // Verify Track
                      bProbeDataRW = TRUE;      // need r/w access
                    else
                      bProbeDataRW = FALSE;     // need r/o access


                    for (i = 0; i < pRWVP->NumSectors; i++)
                    {
                      if (i >= pRWVP->FirstSector)
                        ulDataLen += pRWVP->TrackTable[i].SectorSize;
                    }

                    if (rc = ProbeIt( pDataArea,
                                      ulDataLen,
                                      bProbeDataRW ))
                    {
                      bCallout = FALSE;
                      break;
                    }
                 }

                 bProbeData = FALSE;       // Done probing data.

                 break;

            case IODC_ST:             // 66-Drive Status        (cat 8)

                 if (pParmList)
                 {
                   if (-1L == SFN)           // No previous file handle
                     ulParmLen = 2L;         //   so add byte for drive unit.
                 }

                 if (NULL == pDataArea)
                    bCallout = FALSE;       // Good addresses required.
                 else
                    ulDataLen = sizeof(DDI_DsktRemovMediaCtl_data);

                 break;

               default:

                 // Don't invoke callout if we don't recognize it because
                 //  we don't know what the data or parms are and can't
                 //  safely probe the addresses?

                 bCallout   = FALSE;
                 break;

            } // end switch

            if (!bCallout)
               break;                 // Exit While loop.

            if (bProbeParm)
            {
              if (rc = ProbeIt( pParmList,
                                ulParmLen,
                                bProbeParmRW ))
                 break;               // Exit While loop.
            }

            if (bProbeData)
            {
               if (rc = ProbeIt( pDataArea,
                                 ulDataLen,
                                 bProbeDataRW ))
                 break;               // Exit While loop.
            }

            rc = (*ptempSecIm->SecImp.DEVIOCTL)( SFN,
                                                 Category,
                                                 Function,
                                                 pParmList,
                                                 ulParmLen,
                                                 pDataArea,
                                                 ulDataLen,
                                                 DiskNumber );

      } // SecImp.DEVIOCTL != NULL

      ptempSecIm = ptempSecIm->pNextSecIm;

   } // End While

   return rc;
}

/*------------------------------------------------------------------------
 *
 * ProbeIt - Issue ProbeBuff to validate storage
 *
 *   ENTRY:    pBuffer  - pointer to buffer to probe
 *             ulLength - length to probe
 *             flags    - read or read/write access
 *
 *   EXIT:     0: successful
 *            !0: unsuccessful
 *
 *-----------------------------------------------------------------------*/
ULONG ProbeIt ( PUCHAR pBuffer,
                ULONG  ulLength,
                BOOL   bReadWrite )
{


   ULONG flags = bReadWrite ? TK_UM_WRITE : TK_UM_READ;

   ASSERT_NOSWAP;
   return ( TKProbeBuff(pBuffer, ulLength, flags) );
}



/*
 ** SecHlpFindNext -- Find next helper
 *
 *  Fields used in Find Parms
 *      pszPath     = Don't Care
 *      ulHandle    = Search Handle
 *      rc          = Don't Care
 *      pResultCnt  = Pointer to count of items returned
 *      ulLevel     = Dont't Care (already accounted for in handle)
 *      ulBufferSize= IN = Buffersize , OUT = Bytes Returned?
 *      pcBuffer    = pointer to output buffer
 *      fPosition   = Do we use position info on this call?
 *      if (fPosition)
 *         Position = Number returned from previous Find call
 *         pszPosition = name associated with position number
 *
*/
ULONG _syscall SecHlpFindNext( PFINDPARMS pParms)
{
   ULONG rc;
   USHORT SelBuff = 0;
   USHORT SelFile = 0;
   PCHAR  p16Buffer;
   ULONG SearchCnt;
   CHAR  TCBTmp;

   ASSERT_NOSWAP;

   rc = SELLdrDesc((ULONG)pParms->pcBuffer, pParms->usBufSize, SSToDS(&SelBuff));
   if (rc) {
      return(rc);
   }
   p16Buffer =(PCHAR)(SelBuff << 16);

   pTCBCur->TCBSecFlags |= TCBSEC_NOCALLOUT;  // shut off callouts

   if (pParms->fPosition) {
      // we need a 16:16 pointer to search count so allocate one one the
      // stack and don't sstods the address
      SearchCnt =  *pParms->pResultCnt;

      // Set TCBfSharing to -1 to indicate position info will be used
      TCBTmp = pTCBCur->TCBfSharing;
      pTCBCur->TCBfSharing = -1;

      // Need to convert filename to 16:16 buffer
      rc = SELLdrDesc((ULONG)pParms->pszPosition, strlen(pParms->pszPosition) + 1, SSToDS(&SelFile));
      if (!rc) {
         rc = FindNextHlp2(pParms->ulHandle,(PUSHORT) &SearchCnt, p16Buffer,
                    pParms->usBufSize, pParms->Position,(char *)(SelFile << 16));
         if (!rc) {
            *pParms->pResultCnt = (USHORT) SearchCnt;
         }
         SELFreeGDT(SelFile);

      }  // endif sucessful SELLdrDesc

      pTCBCur->TCBfSharing = TCBTmp;

   }  else {
      rc = FindNextHlp(pParms->ulHandle, pParms->pResultCnt, pParms->usLevel,
                  p16Buffer,pParms->usBufSize);
   }

   pTCBCur->TCBSecFlags &= ~TCBSEC_NOCALLOUT;       // turn on callouts
   SELFreeGDT(SelBuff);

   return(rc);
}



/*
 *** Sec32AuditHook
 *
 *   Invokes audit hook callout for SCS API completion
 *   and informs all registered subsystems of SCS API completion status.
 *
 *   Called via DHSEC_SCSAUDITHOOK (devhlp interface).
 *
 */
VOID Sec32AuditHook ( PCGARGS      pArgs,
                      PRP_GENIOCTL pRemovedParmPacket,
                      ULONG        AuditRC )
{
pSecurityImport2       ptempSecIm;
PRP_GENIOCTL           pParmPacket;
PULONG                 pData;
PULONG                 pParms;
PSESSCSTAT             pSESSCStatus;
RESET_THREAD_PARMS    *pResetParms;
CREATE_INSTANCE_DATA  *pCreateParms;
#ifdef TDLL
REGISTER_TDLL_PARMS   *pRegisterParms;
PGETNEXTTDLL_DATA      pGetNextData;
#endif
RESERVE_SUBJECT_HANDLE_PARMS  *pReserveParms;
RELEASE_SUBJECT_HANDLE_PARMS  *pReleaseParms;
ULONG                  ulKBDMonValue;                               // 120549

   ptempSecIm = pSecIm_list_head;     // All cases need this.

   pRemovedParmPacket;     // Unused for now. Avoid compiler warning.

   pParmPacket = (PRP_GENIOCTL) pArgs->Parm3;

   ASSERT_NOSWAP;

   switch (pArgs->Parm2)
   {
       /*
        *  ------------------------------------------------------------------
        *  SCS (SES) APIs follow.
        *  Code is in ScsDispatchFunction (dispatch.c)
        *  Note that there is a dependency that the defines for the LSS APIs
        *  are mutually exclusive with the SCS APIs.
        *  ------------------------------------------------------------------
        */

       /*
        *  ------------------------------------------------------------------
        *  SESCreateSubjectHandle( pSubjectInfo )
        *                               A
        *                               |
        *        pParmPacket->        Parm[0]
        *  ------------------------------------------------------------------
        */
       case CREATE_SUBJECT_HANDLE  :
       case CREATE_SES_HANDLE      :

         pParms = (PULONG) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.CREATESUBJECTHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.CREATESUBJECTHANDLE)
                                         ( AuditRC,
                                           ((PSUBJECTINFO) pParms[0])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESDeleteSubjectHandle( SubjectHandle )
        *                               A
        *                               |
        *        pParmPacket->        Parm[0]
        *  ------------------------------------------------------------------
        */
       case DELETE_SUBJECT_HANDLE  :
       case DELETE_SES_HANDLE      :

         pParms = (PULONG) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.DELETESUBJECTHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.DELETESUBJECTHANDLE)
                                         ( AuditRC,
                                           (HSUBJECT) pParms[0]
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESSetSubjectHandle( TargetSubject, SubjectHandle )
        *                             A              A
        *                             |              |
        *        pParmPacket->      Parm[1]        Parm[0]
        *  ------------------------------------------------------------------
        */
       case SET_SUBJECT_HANDLE     :
       case SET_SES_HANDLE         :

         pParms = (PULONG) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.SETSUBJECTHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.SETSUBJECTHANDLE)
                                         ( AuditRC,
                                           (ULONG) pParms[1],
                                           (HSUBJECT) pParms[0]
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESSetContextStatus( ContextStatus )
        *                            A
        *                            |
        *        pParmPacket->     Parm[0]
        *  ------------------------------------------------------------------
        */
       case SET_CONTEXT_STATUS     :
       case REQUEST_SES_AUTHORITY  :

         pParms = (PULONG) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.SETCONTEXTSTATUS != NULL)
            {
               (*ptempSecIm->SecImp.SETCONTEXTSTATUS)
                                         ( AuditRC,
                                           (ULONG) pParms[0]
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESSetSecurityContext( pSecurityContext )
        *                               A
        *                               |
        *        pParmPacket->        Parm[0]
        *  ------------------------------------------------------------------
        */
       case SET_SECURITY_CONTEXT   :
       case SET_SES_ATTRIBUTES     :

         pParms = (PULONG) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.SETSECURITYCONTEXT != NULL)
            {
               (*ptempSecIm->SecImp.SETSECURITYCONTEXT)
                                         ( AuditRC,
                                           ((PSECURITYCONTEXT) pParms[0])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQuerySubjectHandle( pid, TargetSubject, pSubjectHandle )
        *                          A         A              A
        *                          |         |              |
        *        pParmPacket->   Parm[0]   Parm[1]        Data[4]
        *  ------------------------------------------------------------------
        */
       case QUERY_SUBJECT_HANDLE   :
       case QUERY_SES_HANDLE       :

         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYSUBJECTHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.QUERYSUBJECTHANDLE)
                                         ( AuditRC,
                                           (PID)   pParms[0],
                                           (ULONG) pParms[1],
                                           (*(PHSUBJECT) pData[4])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQuerySubjectInfo( pid, TargetSubject, pSubjectInfo )
        *                        A         A              A
        *                        |         |              |
        *        pParmPacket-> Parm[0]   Parm[1]        Data[5]
        *  ------------------------------------------------------------------
        */
       case QUERY_SUBJECT_INFO     :
       case QUERY_USER_INFORMATION :

         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYSUBJECTINFO != NULL)
            {
               (*ptempSecIm->SecImp.QUERYSUBJECTINFO)
                                         ( AuditRC,
                                           (PID)   pParms[0],
                                           (ULONG) pParms[1],
                                           ((PSUBJECTINFO) pData[5])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQueryContextStatus( pid, pContextStatus )
        *                          A         A
        *                          |         |
        *        pParmPacket->   Parm[0]   Data[4]
        *  ------------------------------------------------------------------
        */
       case QUERY_CONTEXT_STATUS   :
       case QUERY_SES_AUTHORITY    :

         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYCONTEXTSTATUS != NULL)
            {
               (*ptempSecIm->SecImp.QUERYCONTEXTSTATUS)
                                         ( AuditRC,
                                           (PID)   pParms[0],
                                           (*(PULONG) pData[4])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQuerySecurityContext( pid, pSecurityContext )
        *                            A         A
        *                            |         |
        *        pParmPacket->     Parm[0]   Data[4]
        *  ------------------------------------------------------------------
        */
       case QUERY_SECURITY_CONTEXT :
       case QUERY_SES_ATTRIBUTES   :

         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYSECURITYCONTEXT != NULL)
            {
               (*ptempSecIm->SecImp.QUERYSECURITYCONTEXT)
                                         ( AuditRC,
                                           (PID)   pParms[0],
                                           ((PSECURITYCONTEXT) pData[4])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESControlProcessCreation( ulActionCode )
        *                                  A
        *                                  |
        *        pArgs->                 Parm4
        *  ------------------------------------------------------------------
        */
       case CONTROL_PROCESS:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.CONTROLPROCESSCREATION != NULL)
            {
               (*ptempSecIm->SecImp.CONTROLPROCESSCREATION)
                                         ( AuditRC,
                                           (ULONG) pArgs->Parm4
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESInactivityNotify( timeout )
        *                          A
        *                          |
        *        pArgs->         Parm4
        *  ------------------------------------------------------------------
        */
       case INACTIVITY_NOTIFY:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.INACTIVITYNOTIFY != NULL)
            {
               (*ptempSecIm->SecImp.INACTIVITYNOTIFY)
                                         ( AuditRC,
                                           (ULONG) pArgs->Parm4
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESKillProcess( pid )
        *                   A
        *                   |
        *        pArgs->  Parm4
        *  ------------------------------------------------------------------
        */
       case KILL_PROCESS:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.KILLPROCESS != NULL)
            {
               (*ptempSecIm->SecImp.KILLPROCESS)
                                         ( AuditRC,
                                           (PID) pArgs->Parm4
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQueryProcessInfo( ulActionCode, CUH, pulProcessCount, pProcessBuf )
        *                             A        A          A              A
        *                             |        |          |              |
        *        pArgs->            Parm4    Parm5      Parm6          Parm7
        *  ------------------------------------------------------------------
        */
       case QUERY_PROCESS_IDS:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYPROCESSINFO != NULL)
            {
               (*ptempSecIm->SecImp.QUERYPROCESSINFO)
                                         ( AuditRC,
                                           (ULONG) pArgs->Parm4,
                                           (HSUBJECT) pArgs->Parm5,
                                           (*(PULONG) pArgs->Parm6),
                                           (PVOID) pArgs->Parm7
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQueryAuthorityID( pszAuthorityTag, pAuthorityId)
        *                             A               A
        *                             |               |
        *        pParmPacket->      Parm[0]         Data[4]
        *  ------------------------------------------------------------------
        */
       case QUERY_AUTHORITY_ID     :

         // The authority id is obtained from SESSCStatus.AuthorityID
         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;

         pSESSCStatus = (PSESSCSTAT) pData[4];

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYAUTHORITYID != NULL)
            {
               (*ptempSecIm->SecImp.QUERYAUTHORITYID)
                                         ( AuditRC,
                                           (PUCHAR) pParms[0],
                                           pSESSCStatus->AuthorityID
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESQuerySubjectHandleInfo( SubjectHandle, pSubjectInfo )
        *                                  A               A
        *                                  |               |
        *        pParmPacket->           Parm[0]         Data[4]
        *  ------------------------------------------------------------------
        */
       case QUERY_SUBJECT_HANDLE_INFO  :

         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.QUERYSUBJECTHANDLEINFO != NULL)
            {
               (*ptempSecIm->SecImp.QUERYSUBJECTHANDLEINFO)
                                         ( AuditRC,
                                           (HSUBJECT) pParms[0],
                                           ((PSUBJECTINFO) pData[4])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESResetThreadContext( TargetContext )
        *                              A
        *                              |
        *        pParmPacket->  Parm[0]->RESET_THREAD_PARMS
        *  ------------------------------------------------------------------
        */
       case RESET_THREAD_CONTEXT    :

         pResetParms = (RESET_THREAD_PARMS *) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.RESETTHREADCONTEXT != NULL)
            {
               (*ptempSecIm->SecImp.RESETTHREADCONTEXT)
                                         ( AuditRC,
                                           pResetParms->TargetContext
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESCreateInstanceHandle( pSubjectHandle )
        *                                A
        *                                |
        *        pParmPacket->  Parm[0]->CREATE_INSTANCE_DATA
        *  ------------------------------------------------------------------
        */
       case CREATE_INSTANCE_HANDLE :

         pCreateParms = (CREATE_INSTANCE_DATA *) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.CREATEINSTANCEHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.CREATEINSTANCEHANDLE)
                                         ( AuditRC,
                                           pCreateParms->handle
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESReserveSubjectHandle( TargetSubject )
        *                                A
        *                                |
        *        pParmPacket->  Parm[0]->RESERVE_SUBJECT_HANDLE_PARMS
        *  ------------------------------------------------------------------
        */
       case RESERVE_SUBJECT_HANDLE :

         pReserveParms = (RESERVE_SUBJECT_HANDLE_PARMS *) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.RESERVESUBJECTHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.RESERVESUBJECTHANDLE)
                                         ( AuditRC,
                                           pReserveParms->whichone
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESReleaseSubjectHandle( TargetSubject, SubjectHandle )
        *                                A              A
        *                                |              |
        *        pParmPacket->  Parm[0]->RELEASE_SUBJECT_HANDLE_PARMS
        *  ------------------------------------------------------------------
        */
       case RELEASE_SUBJECT_HANDLE :

         pReleaseParms = (RELEASE_SUBJECT_HANDLE_PARMS *) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.RELEASESUBJECTHANDLE != NULL)
            {
               (*ptempSecIm->SecImp.RELEASESUBJECTHANDLE)
                                         ( AuditRC,
                                           pReleaseParms->whichset,
                                           (HSUBJECT)pReleaseParms->handle
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

#ifdef TDLL
       /*
        *  ------------------------------------------------------------------
        *  SESGetNextTDLL( pszTDLLName, pContextStatus, pTDLLNameLen )
        *                      A              A             A
        *                      |              |             |
        *        pParmPacket->Data[0]->GETNEXTTDLL_DATA  ---+
        *  ------------------------------------------------------------------
        */
       case GET_NEXT_TDLL :

         pGetNextData = (PGETNEXTTDLL_DATA) pParmPacket->DataPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.GETNEXTTDLL != NULL)
            {
               (*ptempSecIm->SecImp.GETNEXTTDLL)
                                         ( AuditRC,
                                           pGetNextData->pszTDLLName,
                                           *(pGetNextData->pContextStatus),
                                           *(pGetNextData->pTDLLNameLen)
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESRegisterTDLL( pszTDLLName, TDLLAuthority, mtehandle )
        *                      A              A             A
        *                      |              |             |
        *        pParmPacket->Parm[0]->REGISTER_TDLL_PARMS -+
        *  ------------------------------------------------------------------
        */
       case REGISTER_TDLL :

         pRegisterParms = (REGISTER_TDLL_PARMS *) pParmPacket->ParmPacket;
         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.REGISTERTDLL != NULL)
            {
               (*ptempSecIm->SecImp.REGISTERTDLL)
                                         ( AuditRC,
                                           pRegisterParms->dll_name,
                                           pRegisterParms->ul_authority,
                                           pRegisterParms->mte_handle
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;
#endif

       case SES_CONTROL  :

          /*
           *  ------------------------------------------------------------------
           * Note: We only get called for:
           *
           *        SESCreateHandleNotify - subfunction 0
           *        SESDeleteHandleNotify - subfunction 1
           *
           *       BUGBUG: There is currently no header file which defines
           *               these subfunctions. dispatch.c does the
           *               defines, and uisctl.c currently hardcodes
           *               values of 0 and 1.
           *  ------------------------------------------------------------------
           */


         /*
          *  ------------------------------------------------------------------
          *  SESCreateHandleNotify( pSubjectInfo )
          *                              A
          *                              |
          *        pParmPacket->     DataArea[7]
          *  ------------------------------------------------------------------
          */
         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;
         if (pParms[1] == 0L)                         // Create (subfunction)
         {
           while (ptempSecIm != NULL )
           {
             if ((ULONG) ptempSecIm->SecImp.CREATEHANDLENOTIFY != NULL)
             {
               (*ptempSecIm->SecImp.CREATEHANDLENOTIFY)
                                         ( AuditRC,
                                           ((PSUBJECTINFO) pData[7])
                                         );
             }
             ptempSecIm = ptempSecIm->pNextSecIm;
           }
         }
         /*
          *  ------------------------------------------------------------------
          *  SESDeleteHandleNotify( pSubjectHandle )
          *                               A
          *                               |
          *        pParmPacket->      DataArea[5]
          *  ------------------------------------------------------------------
          */
         else                                         // Delete (subfunction)
         {
           while (ptempSecIm != NULL )
           {
             if ((ULONG) ptempSecIm->SecImp.DELETEHANDLENOTIFY != NULL)
             {
               (*ptempSecIm->SecImp.DELETEHANDLENOTIFY)
                                         ( AuditRC,
                                           (*(PHSUBJECT) pData[5])
                                         );
             }
             ptempSecIm = ptempSecIm->pNextSecIm;
           }
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESControlKBDMonitors( ActionCode, pStatus )
        *                            A           A
        *                            |           |
        *        pParmPacket->     Parm[0]     Data[4]
        *  ------------------------------------------------------------------
        */
       case CONTROL_KBD_MONITORS  :

         pParms = (PULONG) pParmPacket->ParmPacket;
         pData  = (PULONG) pParmPacket->DataPacket;

         if ((ULONG) pParms[0] != 2L)           // Not a query.        120549
            ulKBDMonValue = 0L;                 // Parm not used.      120549
         else                                   //                     120549
            ulKBDMonValue = *(PULONG) pData[4]; // Pass current value. 120549

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.CONTROLKBDMONITORS != NULL)
            {
               (*ptempSecIm->SecImp.CONTROLKBDMONITORS)
                                         ( AuditRC,
                                           (ULONG) pParms[0],
                                           (*(PULONG) pData[4])
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  LSS APIs follow.
        *  Code is in LssDispatchFunction (dispatch.c)
        *  Note that there is a dependency that the defines for the LSS APIs
        *  are mutually exclusive with the SCS APIs.
        *  ------------------------------------------------------------------
        */

       /*
        *  ------------------------------------------------------------------
        *  SESReturnEventStatus( pSESEventInfo )
        *                             A
        *                             |
        *        pArgs->            Parm3
        *  ------------------------------------------------------------------
        */
       case SES_RETURN_EVENT:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.RETURNEVENTSTATUS != NULL)
            {
               (*ptempSecIm->SecImp.RETURNEVENTSTATUS)
                                         ( AuditRC,
                                           ((PSESEVENT) pArgs->Parm3)
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;

         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESWaitEvent( pSESEventInfo, Timeout )
        *                      A           A
        *                      |           |
        *        pArgs->     Parm3       Parm4
        *  ------------------------------------------------------------------
        */
       case SES_WAIT_EVENT:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.WAITEVENT != NULL)
            {
               (*ptempSecIm->SecImp.WAITEVENT)
                                         ( AuditRC,
                                           ((PSESEVENT) pArgs->Parm3),
                                           (ULONG) pArgs->Parm4
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESStartEvent( pSESStartEvent )
        *                      A
        *                      |
        *        pArgs->     Parm3
        *  ------------------------------------------------------------------
        */
       case SES_START_EVENT:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.STARTEVENT != NULL)
            {
               (*ptempSecIm->SecImp.STARTEVENT)
                                         ( AuditRC,
                                           ((PSESSTARTEVENT) pArgs->Parm3)
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESRegisterDaemon( pDaemonId, EventList )
        *                         A          A
        *                         |          |
        *        pArgs->        Parm3      Parm4
        *  ------------------------------------------------------------------
        */
       case SES_REGISTER_SCA_DAEMON:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.REGISTERDAEMON != NULL)
            {
               (*ptempSecIm->SecImp.REGISTERDAEMON)
                                         ( AuditRC,
                                           *(PULONG) pArgs->Parm3,
                                           (ULONG) pArgs->Parm4
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       /*
        *  ------------------------------------------------------------------
        *  SESReturnWaitEvent( pSESEventInfo, Timeout )
        *                            A           A
        *                            |           |
        *        pArgs->           Parm3       Parm4
        *  ------------------------------------------------------------------
        */
       case SES_RETURNWAIT_EVENT:

         while (ptempSecIm != NULL )
         {
            if ((ULONG) ptempSecIm->SecImp.RETURNWAITEVENT != NULL)
            {
               (*ptempSecIm->SecImp.RETURNWAITEVENT)
                                         ( AuditRC,
                                           ((PSESEVENT) pArgs->Parm3),
                                           (ULONG) pArgs->Parm4
                                         );
            }
            ptempSecIm = ptempSecIm->pNextSecIm;
         }
         break;

       default:    // Unrecognized. No callout.
         break;

   } // end switch

   return;
}


/*
 *** Sec32GetInfo
 *
 *       Called By:  SecDevRoute
 *
 *       Fill in user buffer with system information from the PTDA.
 *
 *       DS & ES are flat on entry (set up by SecDevRoute).
 *
 */
VOID Sec32GetInfo( PPTDA pPTDA, PSECGETINFO pGetInfoBuf )
{
  USHORT cnest;                                                    // 132809

  if (pPTDA)
  {

   // Fill in all of the fields in SecGetInfo_s
   pGetInfoBuf->sgi_pPTDAParent      = (PVOID) pPTDA->pPTDAParent;
   pGetInfoBuf->sgi_pPTDAFirstChild  = (PVOID) pPTDA->pPTDAFirstChild;
   pGetInfoBuf->sgi_pPTDAExecChild   = (PVOID) pPTDA->pPTDAExecChild;
   pGetInfoBuf->sgi_pPTDANextSibling = (PVOID) pPTDA->pPTDANextSibling;
   pGetInfoBuf->sgi_pPTDAPrevSibling = (PVOID) pPTDA->pPTDAPrevSibling;
   pGetInfoBuf->sgi_ptda_pszproc     = (PVOID) pPTDA->ptda_pszproc;
   if (pGetInfoBuf->sgi_ptda_pszproc == NULL) {
      pGetInfoBuf->sgi_ptda_pszproc = "sysinit";
   }
   pGetInfoBuf->sgi_ptda_pTCBHead    = (PVOID) pPTDA->ptda_pTCBHead;
   pGetInfoBuf->sgi_CurrTCB          = pPTDA->CurrTCB;

   // Copy the entire infoseg.
   memcpy( &(pGetInfoBuf->sgi_ptda_infoseg),
           &(pPTDA->ptda_infoseg),
           LIS_LEN );
  }

  KSEMQueryMutex( (HKSEM)&LdrSem, SSToDS(&cnest) );

  pGetInfoBuf->sgi_sem_held = (ULONG) cnest;

   return;
}


// WARNING -- Everything below this line will dissappear after init time
#pragma  code_seg("CINIT_TEXT","CODE")
#pragma  data_seg("CINIT_DATA", "CINITDATA")

static SecurityExport SecEx = {
   SEC_EXPORT_MAJOR_VERSION,        // Major Version
   SEC_EXPORT_MINOR_VERSION,        // Minor Version
   (ULONG)SecRead,                  // SecurityRead
   (ULONG)SecWrite,                 // SecurityWrite
   (ULONG)SecOpen,                  // SecurityOpen
   (ULONG)SecClose,                 // SecurityClose
   (ULONG)SecQFileSize,             // SecurityQFileSize
   (ULONG)SecChgFilePtr,            // DosChgFilePtr
   (ULONG)SecSFFromSFN,             // DosChgFilePtr
   (ULONG)SecHlpFindNext,           // SecurityFindNext Helper           DGE02
   (ULONG)SecPathFromSFN,           // Given an SFN, return pathname
   (ULONG*)&apdemSVC                // Dos MVDM SVC dispatch table       DGE02
};



/*
 *** Sec1 (SecurityGetExport)
 *
 *   Returns a table of security Helpers for the security subsystem to
 *   call.
 *
 *
*/
ULONG Sec1 ( pSecurityExport pExport )
{
   if (pExport->seVersionMajor == SEC_EXPORT_MAJOR_VERSION) {
      memcpy(pExport,&SecEx,sizeof(SecurityExport));
   }
   return 0;
}


/*
 *** Sec2 (SecuritySetImport)
 *
 *   This is where the security subsystem provides us with a table of
 *   callouts that we call when significant events occur.
 *
 *   Each time this routine is called, an Import structure is allocated
 *   and added to a linked list.  The list is used by the Sec32* routines
 *   so that (potentially) multiple subsystems can receive  FSD_ callouts.
 *   Currently, access to the list is controlled by disabling the DevHelp
 *   SetImport function after the FIRST subsystem initializes.  (So this
 *   routine would only be called once).
 *
 *   Note that the SecurityImport structure which is passed in does
 *   not contain the pNextSecIm pointer.
*/
ULONG Sec2( pSecurityImport pImport )
{
   ULONG   rc;
   pSecurityImport2 pSecIm;
//   pSecurityImport2 ptempSecIm;      No longer needed.


   //fSec = FALSE; // do we need to disable callouts while we add a 2nd sub?

   // Allocate memory
   // 104809  Using swappable data leaves us open to page fault on
   //         swapper operation...   chg from _SWAPRW

   if (( rc = VMAllocKHB( VM_HKH_PUB_RESRW,                   //104809
                         sizeof(SecurityImport2),
                         (VMHOB)FSBUFOWNER,
                         0,
                         SSToDS(&pSecIm) ) ) != 0 )
   {
       return(ERROR_NOT_ENOUGH_MEMORY);
   }


   // Copy callout addresses
   if (pImport->siVersionMajor == SEC_IMPORT_MAJOR_VERSION) {
      // Only copy length of SecurityImport (not SecurityImport2)
      memcpy(pSecIm, pImport, sizeof(SecurityImport));
      pSecIm->pNextSecIm = NULL;
   }
   else {
      return(ERROR_GEN_FAILURE);
   }

   /*
    * If security subsystem provides an address for CallGate handlers,
    * go ahead and hook them.
    *
    * NOTE:  Only 1 security subsystem can HOOK CallGates!!
    *        Since PDF won't be using CallGates, this isn't a problem...
    *        If we ever allow multiple subsystems (excluding PDF) this
    *        will have to be looked at.
   */

   if ((ULONG)pSecIm->SecImp.CALLGATE32 != 0) {
      SecHook32CG((ULONG)*pSecIm->SecImp.CALLGATE32);
   }
   if ((ULONG)pSecIm->SecImp.CALLGATE16 != 0) {
      SecHook16CG((ULONG)*pSecIm->SecImp.CALLGATE16);
   }


   // Add to list
   if (pSecIm_list_head == NULL) {                // If list_head is NULL,
      pSecIm_list_head = pSecIm;                  //    point head to us
   }
   else {
      // BUGBUG!! Switched the insertion order to add caller at
      // 5/95     the beginning of the list.
      //          Since SESDD is not yet a BASEDEV, we load last.
      //     !!!  When SESDD becomes a BASEDEV, this must be put back
      //          to insert at end of the list.

      pSecIm->pNextSecIm = pSecIm_list_head;
      pSecIm_list_head = pSecIm;                  //    point head to us
   }

// Insert new ISS structure at END of list
// else {
//    ptempSecIm = pSecIm_list_head;
//    while (ptempSecIm->pNextSecIm != NULL) {    // Is there a next?
//           ptempSecIm = ptempSecIm->pNextSecIm; //    Yes, keep looping
//    }
//    ptempSecIm->pNextSecIm = pSecIm;            // No, add us to list
// }

   fSec = TRUE;                                   // Done. Enable callouts
   return 0;

}

