/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    common.cpp

Abstract:

    Shared APIs

Author:

    Jin Huang

Revision History:

    jinhuang        23-Jan-1998   merged from multiple modules

--*/
#include "headers.h"
#include <alloca.h>

#if DBG

    DEFINE_DEBUG2(Sce);

    DEBUG_KEY   SceDebugKeys[] = {{DEB_ERROR,        "Error"},
                                 {DEB_WARN,          "Warn"},
                                 {DEB_TRACE,         "Trace"},
                                 {0,                 NULL}};


    VOID
    DebugInitialize()
    {
        SceInitDebug(SceDebugKeys);
    }

    VOID
    DebugUninit()
    {
        SceUnloadDebug();
    }

#endif // DBG

HINSTANCE MyModuleHandle=NULL;
HANDLE  hEventLog = NULL;
TCHAR EventSource[64];
const TCHAR c_szCRLF[]    = TEXT("\r\n");

#define RIGHT_DS_CREATE_CHILD     ACTRL_DS_CREATE_CHILD
#define RIGHT_DS_DELETE_CHILD     ACTRL_DS_DELETE_CHILD
#define RIGHT_DS_DELETE_SELF      DELETE
#define RIGHT_DS_LIST_CONTENTS    ACTRL_DS_LIST
#define RIGHT_DS_SELF_WRITE       ACTRL_DS_SELF
#define RIGHT_DS_READ_PROPERTY    ACTRL_DS_READ_PROP
#define RIGHT_DS_WRITE_PROPERTY   ACTRL_DS_WRITE_PROP

//
// defines for DSDIT, DSLOG, SYSVOL registry path
//
#define szNetlogonKey    TEXT("System\\CurrentControlSet\\Services\\Netlogon\\Parameters")
#define szNTDSKey        TEXT("System\\CurrentControlSet\\Services\\NTDS\\Parameters")
#define szSetupKey       TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup")
#define szSysvolValue    TEXT("SysVol")
#define szDSDITValue     TEXT("DSA Working Directory")
#define szDSLOGValue     TEXT("Database log files path")
#define szBootDriveValue TEXT("BootDir")

//
// Define the generic rights
//

// generic read
#define GENERIC_READ_MAPPING     ((STANDARD_RIGHTS_READ)     | \
                                  (ACTRL_DS_LIST)   | \
                                  (ACTRL_DS_READ_PROP))

// generic execute
#define GENERIC_EXECUTE_MAPPING  ((STANDARD_RIGHTS_EXECUTE)  | \
                                  (ACTRL_DS_LIST))
// generic right
#define GENERIC_WRITE_MAPPING    ((STANDARD_RIGHTS_WRITE)    | \
                                  (ACTRL_DS_SELF)      | \
                                  (ACTRL_DS_WRITE_PROP))
// generic all

#define GENERIC_ALL_MAPPING      ((STANDARD_RIGHTS_REQUIRED) | \
                                  (ACTRL_DS_CREATE_CHILD)    | \
                                  (ACTRL_DS_DELETE_CHILD)    | \
                                  (ACTRL_DS_READ_PROP)   | \
                                  (ACTRL_DS_WRITE_PROP)  | \
                                  (ACTRL_DS_LIST)   | \
                                  (ACTRL_DS_SELF))

GENERIC_MAPPING DsGenMap = {
                        GENERIC_READ_MAPPING,
                        GENERIC_WRITE_MAPPING,
                        GENERIC_EXECUTE_MAPPING,
                        GENERIC_ALL_MAPPING
                        };

DWORD
ScepCompareExplicitAcl(
    IN SE_OBJECT_TYPE ObjectType,
    IN BOOL IsContainer,
    IN PACL pAcl1,
    IN PACL pAcl2,
    OUT PBOOL pDifferent
    );

NTSTATUS
ScepAnyExplicitAcl(
    IN PACL Acl,
    IN DWORD Processed,
    OUT PBOOL pExist
    );

BOOL
ScepEqualAce(
    IN SE_OBJECT_TYPE ObjectType,
    IN BOOL IsContainer,
    IN ACE_HEADER *pAce1,
    IN ACE_HEADER *pAce2
    );

DWORD
ScepGetCurrentUserProfilePath(
    OUT PWSTR *ProfilePath
    );

DWORD
ScepGetUsersProfileName(
    IN UNICODE_STRING AssignedProfile,
    IN PSID AccountSid,
    IN BOOL bDefault,
    OUT PWSTR *UserProfilePath
    );

BOOL
ScepConvertSDDLAceType(
    LPTSTR  pszValue,
    PCWSTR  szSearchFor,  // only two letters are allowed
    PCWSTR  szReplace
    );
BOOL
ScepConvertSDDLSid(
    LPTSTR  pszValue,
    PCWSTR  szSearchFor,  // only two letters are allowed
    PCWSTR  szReplace
    );

NTSTATUS
ScepConvertAclBlobToAdl(
    IN      SE_OBJECT_TYPE  ObjectType,
    IN      BOOL    IsContainer,
    IN      PACL    pAcl,
    OUT     DWORD   *pdwAceNumber,
    OUT     BOOL    *pbAclNoExplicitAces,
    OUT     PSCEP_ADL_NODE *hTable
    );

DWORD
ScepAdlLookupAdd(
    IN      SE_OBJECT_TYPE ObjectType,
    IN      BOOL IsContainer,
    IN      ACE_HEADER   *pAce,
    OUT     PSCEP_ADL_NODE *hTable
    );

PSCEP_ADL_NODE
ScepAdlLookup(
    IN  ACE_HEADER   *pAce,
    IN  PSCEP_ADL_NODE *hTable
    );


DWORD
ScepAddToAdlList(
    IN      SE_OBJECT_TYPE ObjectType,
    IN      BOOL    IsContainer,
    IN      ACE_HEADER *pAce,
    OUT     PSCEP_ADL_NODE *pAdlList
    );

VOID
ScepAdlMergeMasks(
    IN  SE_OBJECT_TYPE  ObjectType,
    IN  BOOL    IsContainer,
    IN  ACE_HEADER  *pAce,
    IN  PSCEP_ADL_NODE pNode
    );

BOOL
ScepEqualAdls(
    IN  PSCEP_ADL_NODE *hTable1,
    IN  PSCEP_ADL_NODE *hTable2
    );

VOID
ScepFreeAdl(
    IN    PSCEP_ADL_NODE *hTable
    );

SCESTATUS
ScepFreeAdlList(
   IN PSCEP_ADL_NODE pAdlList
   );

BOOL
ScepEqualSid(
    IN PISID pSid1,
    IN PISID pSid2
    );

PWSTR
ScepMultiSzWcsstr(
    PWSTR   pszStringToSearchIn,
    PWSTR   pszStringToSearchFor
    );

//
// function definitions
//

SCESTATUS
WINAPI
SceSvcpGetInformationTemplate(
    IN HINF hInf,
    IN PCWSTR ServiceName,
    IN PCWSTR Key OPTIONAL,
    OUT PSCESVC_CONFIGURATION_INFO *ServiceInfo
    )
{
    SCESTATUS rc=SCESTATUS_SUCCESS;

    if ( hInf == NULL || hInf == INVALID_HANDLE_VALUE ||
         ServiceName == NULL || ServiceInfo == NULL ) {

        return(SCESTATUS_INVALID_PARAMETER);

    }

    LONG nCount=0;

    if ( Key == NULL ) {
        //
        // get the entire section count
        //
        nCount = SetupGetLineCount(hInf, ServiceName);

        if ( nCount <= 0 ) {
            //
            // the section is not there, or nothing in the section
            //
            rc =SCESTATUS_RECORD_NOT_FOUND;

        }

    } else {

        nCount = 1;
    }

    if ( rc == SCESTATUS_SUCCESS ) {

        //
        // allocate buffer for the section
        //
        *ServiceInfo = (PSCESVC_CONFIGURATION_INFO)ScepAlloc(LMEM_FIXED,
                              sizeof(SCESVC_CONFIGURATION_INFO));

        if ( *ServiceInfo == NULL ) {
            rc = SCESTATUS_NOT_ENOUGH_RESOURCE;

        } else {

            (*ServiceInfo)->Lines =
                (PSCESVC_CONFIGURATION_LINE)ScepAlloc(LMEM_ZEROINIT,
                          nCount*sizeof(SCESVC_CONFIGURATION_LINE));

            if ( (*ServiceInfo)->Lines == NULL ) {

                rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
                ScepFree(*ServiceInfo);
                *ServiceInfo = NULL;

            } else {
                (*ServiceInfo)->Count = nCount;
            }
        }

    }

    if ( rc == SCESTATUS_SUCCESS ) {

        //
        // get each line in the section, nor a single key now
        //
        INFCONTEXT    InfLine;
        DWORD         LineLen, Len, KeyLen, DataSize;
        DWORD         i, cFields;
        DWORD         LineCount=0;
        PWSTR         Keyname=NULL, Strvalue=NULL;


        //
        // look for the first line in the Servi   ceName section
        //
        if(SetupFindFirstLine(hInf,ServiceName,NULL,&InfLine)) {

            do {
                //
                // read each line in the section and append to the end of the buffer
                // note: the required size returned from SetupGetStringField already
                // has one more character space.
                //
                rc = SCESTATUS_INVALID_DATA;

                if ( SetupGetStringField(&InfLine, 0, NULL, 0, &KeyLen) ) {

                    Keyname = (PWSTR)ScepAlloc( LMEM_ZEROINIT, (KeyLen+1)*sizeof(WCHAR));

                    if ( Keyname != NULL ) {

                        if ( SetupGetStringField(&InfLine, 0, Keyname, KeyLen, NULL) ) {

                            //
                            // Got key name, compare with Key if specified.
                            //
                            if ( Key == NULL || _wcsicmp(Keyname, Key) == 0 ) {

                                cFields = SetupGetFieldCount( &InfLine );
                                LineLen = 0;
                                Len = 0;

                                rc = SCESTATUS_SUCCESS;
                                //
                                // count total number of characters for the value
                                //
                                for ( i=0; i<cFields; i++) {

                                    if( SetupGetStringField(&InfLine,i+1,NULL,0,&DataSize) ) {

                                        LineLen += (DataSize + 1);

                                    } else {

                                        rc = SCESTATUS_INVALID_DATA;
                                        break;
                                    }
                                }

                                if ( rc == SCESTATUS_SUCCESS ) {

                                    Strvalue = (PWSTR)ScepAlloc( LMEM_ZEROINIT,
                                                                 (LineLen+1)*sizeof(WCHAR) );

                                    if( Strvalue != NULL ) {

                                        for ( i=0; i<cFields; i++) {

                                            if ( !SetupGetStringField(&InfLine, i+1,
                                                         Strvalue+Len, LineLen-Len, &DataSize) ) {

                                                rc = SCESTATUS_INVALID_DATA;
                                                break;
                                            }

                                            if ( i == cFields-1)
                                                *(Strvalue+Len+DataSize-1) = L'\0';
                                            else
                                                *(Strvalue+Len+DataSize-1) = L',';
                                            Len += DataSize;
                                        }

                                        if ( rc == SCESTATUS_SUCCESS ) {
                                            //
                                            // everything is successful
                                            //
                                            (*ServiceInfo)->Lines[LineCount].Key = Keyname;
                                            (*ServiceInfo)->Lines[LineCount].Value = Strvalue;
                                            (*ServiceInfo)->Lines[LineCount].ValueLen = LineLen*sizeof(WCHAR);

                                            Keyname = NULL;
                                            Strvalue = NULL;
                                            rc = SCESTATUS_SUCCESS;

                                            LineCount++;

                                            if ( Key != NULL ) {
                                                break; // break the do while loop because the exact match for the key is found
                                            }

                                        } else {
                                            ScepFree( Strvalue );
                                            Strvalue = NULL;
                                        }

                                    } else
                                        rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
                                }

                            } else {
                                //
                                // did not find the right key, go to next line
                                //
                                rc = SCESTATUS_SUCCESS;
                            }
                        }

                        if ( Keyname != NULL ) {
                            ScepFree(Keyname);
                            Keyname = NULL;
                        }

                    } else {
                        rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
                    }
                }

                if ( rc != SCESTATUS_SUCCESS ) {
                    break;
                }

            } while ( SetupFindNextLine(&InfLine,&InfLine) );

        } else {

            rc = SCESTATUS_RECORD_NOT_FOUND;
        }
        //
        // if no exact match for the key is found, return the error code
        //
        if ( rc == SCESTATUS_SUCCESS && Key != NULL && LineCount == 0 ) {
            rc = SCESTATUS_RECORD_NOT_FOUND;
        }
    }

    if ( rc != SCESTATUS_SUCCESS && *ServiceInfo != NULL ) {
        //
        // free ServiceInfo
        //
        PSCESVC_CONFIGURATION_LINE Lines;

        Lines = ((PSCESVC_CONFIGURATION_INFO)(*ServiceInfo))->Lines;

        for ( DWORD i=0; i<((PSCESVC_CONFIGURATION_INFO)(*ServiceInfo))->Count; i++) {

            if ( Lines[i].Key != NULL ) {
                ScepFree(Lines[i].Key);
            }

            if ( Lines[i].Value != NULL ) {
                ScepFree(Lines[i].Value);
            }
        }

        ScepFree(Lines);
        ScepFree(*ServiceInfo);
        *ServiceInfo = NULL;
    }

    return(rc);

}


DWORD
ScepAddToNameList(
    OUT PSCE_NAME_LIST *pNameList,
    IN PWSTR Name,
    IN ULONG Len
    )
/* ++
Routine Description:

    This routine adds a name (wchar) to the name list. The new added
    node is always placed as the head of the list for performance reason.

Arguments:

    pNameList -  The address of the name list to add to.

    Name      -  The name to add

    Len       -  number of wchars in Name

Return value:

    Win32 error code
-- */
{

    PSCE_NAME_LIST pList=NULL;
    ULONG  Length=Len;

    //
    // check arguments
    //
    if ( pNameList == NULL )
        return(ERROR_INVALID_PARAMETER);

    if ( Name == NULL )
        return(NO_ERROR);

    if ( Len == 0 )
        Length = wcslen(Name);

    if ( Length == 0 )
        return(NO_ERROR);

    //
    // allocate a new node
    //
    pList = (PSCE_NAME_LIST)ScepAlloc( (UINT)0, sizeof(SCE_NAME_LIST));

    if ( pList == NULL )
        return(ERROR_NOT_ENOUGH_MEMORY);

    pList->Name = (PWSTR)ScepAlloc( LMEM_ZEROINIT, (Length+1)*sizeof(TCHAR));
    if ( pList->Name == NULL ) {
        ScepFree(pList);
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // add the node to the front of the list and link its next to the old list
    //
    wcsncpy(pList->Name, Name, Length);
    pList->Next = *pNameList;
    *pNameList = pList;

    return(NO_ERROR);
}



SCESTATUS
ScepBuildErrorLogInfo(
    IN DWORD   rc,
    OUT PSCE_ERROR_LOG_INFO *errlog,
    IN UINT    nId,
//    IN PCWSTR  fmt,
    ...
    )
/* ++

Routine Description:

   This routine add the error information to the end of error log info list
   (errlog). The error information is stored in SCE_ERROR_LOG_INFO structure.

Arguments:

   rc - Win32 error code

   errlog - the error log info list head

   fmt - a format string

   ... - variable arguments

Return value:

   SCESTATUS error code

-- */
{
    PSCE_ERROR_LOG_INFO pNode;
    PSCE_ERROR_LOG_INFO pErr;
    DWORD              bufferSize;
    PWSTR              buf=NULL;
    va_list            args;
    WCHAR              szTempString[256];

    //
    // check arguments
    //
//    if ( errlog == NULL || fmt == NULL )
    if ( errlog == NULL || nId == 0 )
        return(SCESTATUS_SUCCESS);

    szTempString[0] = L'\0';

    LoadString( MyModuleHandle,
                nId,
                szTempString,
                256
                );
    if ( szTempString[0] == L'\0' )
        return(SCESTATUS_SUCCESS);

    SafeAllocaAllocate( buf, SCE_BUF_LEN*sizeof(WCHAR) );

    if ( buf == NULL ) {
        return(SCESTATUS_NOT_ENOUGH_RESOURCE);
    }

    va_start( args, nId );
    vswprintf( buf, szTempString, args );
    va_end( args );

    bufferSize = wcslen(buf);

    //
    // no error information to be stored. return
    //

    SCESTATUS rCode=SCESTATUS_SUCCESS;

    if ( bufferSize != 0 ) {

        //
        // allocate memory and store error information in pNode->buffer
        //

        pNode = (PSCE_ERROR_LOG_INFO)ScepAlloc( 0, sizeof(SCE_ERROR_LOG_INFO) );

        if ( pNode != NULL ) {

            pNode->buffer = (LPTSTR)ScepAlloc( 0, (bufferSize+1)*sizeof(TCHAR) );
            if ( pNode->buffer != NULL ) {

                //
                // Error information is in "SystemMessage : Caller'sMessage" format
                //
                pNode->buffer[0] = L'\0';

                wcscpy(pNode->buffer, buf);

                pNode->rc = rc;
                pNode->next = NULL;

                //
                // link it to the list
                //
                if ( *errlog == NULL )

                    //
                    // This is the first node in the error log information list
                    //

                    *errlog = pNode;

                else {

                    //
                    // find the last node in the list
                    //

                    for ( pErr=*errlog; pErr->next; pErr = pErr->next );
                    pErr->next = pNode;
                }

            } else {

                ScepFree(pNode);
                rCode = SCESTATUS_NOT_ENOUGH_RESOURCE;
            }

        } else {
            rCode = SCESTATUS_NOT_ENOUGH_RESOURCE;
        }

    }

    SafeAllocaFree( buf );

    return(rc);

}


DWORD
ScepRegQueryIntValue(
    IN HKEY hKeyRoot,
    IN PWSTR SubKey,
    IN PWSTR ValueName,
    OUT DWORD *Value
    )
/* ++

Routine Description:

   This routine queries a REG_DWORD value from a value name/subkey.

Arguments:

   hKeyRoot    - root

   SubKey      - key path

   ValueName   - name of the value

   Value       - the output value for the ValueName

Return values:

   Win32 error code
-- */
{
    DWORD   Rcode;
    DWORD   RegType;
    DWORD   dSize=0;
    HKEY    hKey=NULL;

    if(( Rcode = RegOpenKeyEx(hKeyRoot,
                              SubKey,
                              0,
                              KEY_READ,
                              &hKey
                             )) == ERROR_SUCCESS ) {

        if(( Rcode = RegQueryValueEx(hKey,
                                     ValueName,
                                     0,
                                     &RegType,
                                     NULL,
                                     &dSize
                                    )) == ERROR_SUCCESS ) {
            switch (RegType) {
            case REG_DWORD:
            case REG_DWORD_BIG_ENDIAN:

                Rcode = RegQueryValueEx(hKey,
                                       ValueName,
                                       0,
                                       &RegType,
                                       (BYTE *)Value,
                                       &dSize
                                      );
                if ( Rcode != ERROR_SUCCESS ) {

                    if ( Value != NULL )
                        *Value = 0;

                }
                break;

            default:

                Rcode = ERROR_INVALID_DATATYPE;

                break;
            }
        }
    }

    if( hKey )
        RegCloseKey( hKey );

    return(Rcode);
}


DWORD
ScepRegSetIntValue(
    IN HKEY hKeyRoot,
    IN PWSTR SubKey,
    IN PWSTR ValueName,
    IN DWORD Value
    )
/* ++

Routine Description:

   This routine sets a REG_DWORD value to a value name/subkey.

Arguments:

   hKeyRoot    - root

   SubKey      - key path

   ValueName   - name of the value

   Value       - the value to set

Return values:

   Win32 error code
-- */
{
    DWORD   Rcode;
    HKEY    hKey=NULL;

    if(( Rcode = RegOpenKeyEx(hKeyRoot,
                              SubKey,
                              0,
                              KEY_SET_VALUE,
                              &hKey
                             )) == ERROR_SUCCESS ) {

        Rcode = RegSetValueEx( hKey,
                               ValueName,
                               0,
                               REG_DWORD,
                               (BYTE *)&Value,
                               4
                               );

    }

    if( hKey )
        RegCloseKey( hKey );

    return(Rcode);
}

DWORD
ScepRegQueryBinaryValue(
    IN HKEY hKeyRoot,
    IN PWSTR SubKey,
    IN PWSTR ValueName,
    OUT PBYTE *ppValue
    )
/* ++

Routine Description:

   This routine queries a REG_BINARY value from a value name/subkey.

Arguments:

   hKeyRoot    - root

   SubKey      - key path

   ValueName   - name of the value

   Value       - the output value for the ValueName

Return values:

   Win32 error code
-- */
{
    DWORD   Rcode;
    DWORD   RegType;
    DWORD   dSize=0;
    HKEY    hKey=NULL;

    if(( Rcode = RegOpenKeyEx(hKeyRoot,
                              SubKey,
                              0,
                              KEY_READ,
                              &hKey
                             )) == ERROR_SUCCESS ) {

        // get the size since it is free form binary
        if(( Rcode = RegQueryValueEx(hKey,
                                     ValueName,
                                     0,
                                     &RegType,
                                     NULL,
                                     &dSize
                                    )) == ERROR_SUCCESS ) {
            switch (RegType) {
            case REG_BINARY:

                //need to free this outside
                if (ppValue)
                    *ppValue = ( PBYTE )ScepAlloc( 0, sizeof(BYTE) * dSize);

                Rcode = RegQueryValueEx(hKey,
                                       ValueName,
                                       0,
                                       &RegType,
                                       ( PBYTE ) *ppValue,
                                       &dSize
                                      );
                if ( Rcode != ERROR_SUCCESS ) {

                    if ( *ppValue != NULL )
                        **ppValue = (BYTE)0;

                }
                break;

            default:

                Rcode = ERROR_INVALID_DATATYPE;

                break;
            }
        }
    }

    if( hKey )
        RegCloseKey( hKey );

    return(Rcode);
}


DWORD
ScepRegSetValue(
    IN HKEY hKeyRoot,
    IN PWSTR SubKey,
    IN PWSTR ValueName,
    IN DWORD RegType,
    IN BYTE *Value,
    IN DWORD ValueLen
    )
/* ++

Routine Description:

   This routine sets a string value to a value name/subkey.

Arguments:

   hKeyRoot    - root

   SubKey      - key path

   ValueName   - name of the value

   Value       - the value to set

   ValueLen    - The number of bytes in Value

Return values:

   Win32 error code
-- */
{
    DWORD   Rcode;
    DWORD   NewKey;
    HKEY    hKey=NULL;
    SECURITY_ATTRIBUTES     SecurityAttributes;
    PSECURITY_DESCRIPTOR    SecurityDescriptor=NULL;


    if(( Rcode = RegOpenKeyEx(hKeyRoot,
                              SubKey,
                              0,
                              KEY_SET_VALUE,
                              &hKey
                             )) != ERROR_SUCCESS ) {

        SecurityAttributes.nLength              = sizeof( SECURITY_ATTRIBUTES );
        SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
        SecurityAttributes.bInheritHandle       = FALSE;

        Rcode = RegCreateKeyEx(
                   hKeyRoot,
                   SubKey,
                   0,
                   NULL, // LPTSTR lpClass,
                   REG_OPTION_NON_VOLATILE,
                   KEY_WRITE, // KEY_SET_VALUE,
                   NULL, // &SecurityAttributes,
                   &hKey,
                   &NewKey
                  );
    }

    if ( Rcode == ERROR_SUCCESS ) {

        Rcode = RegSetValueEx( hKey,
                               ValueName,
                               0,
                               RegType,
                               Value,
                               ValueLen
                               );

    }

    if( hKey )
        RegCloseKey( hKey );

    return(Rcode);
}

DWORD
ScepRegQueryValue(
    IN HKEY hKeyRoot,
    IN PWSTR SubKey,
    IN PCWSTR ValueName,
    OUT PVOID *Value,
    OUT LPDWORD pRegType
    )
/* ++

Routine Description:

   This routine queries a REG_SZ value from a value name/subkey.
   The output buffer is allocated if it is NULL. It must be freed
   by LocalFree

Arguments:

   hKeyRoot    - root

   SubKey      - key path

   ValueName   - name of the value

   Value       - the output string for the ValueName

Return values:

   Win32 error code
-- */
{
    DWORD   Rcode;
    DWORD   dSize=0;
    HKEY    hKey=NULL;
    BOOL    FreeMem=FALSE;

    if ( SubKey == NULL || ValueName == NULL ||
         Value == NULL || pRegType == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if(( Rcode = RegOpenKeyEx(hKeyRoot,
                              SubKey,
                              0,
                              KEY_READ,
                              &hKey
                             )) == ERROR_SUCCESS ) {

        if(( Rcode = RegQueryValueEx(hKey,
                                     ValueName,
                                     0,
                                     pRegType,
                                     NULL,
                                     &dSize
                                    )) == ERROR_SUCCESS ) {
            switch (*pRegType) {
/*
            case REG_DWORD:
            case REG_DWORD_BIG_ENDIAN:

                Rcode = RegQueryValueEx(hKey,
                                       ValueName,
                                       0,
                                       pRegType,
                                       (BYTE *)(*Value),
                                       &dSize
                                      );
                if ( Rcode != ERROR_SUCCESS ) {

                    if ( *Value != NULL )
                        *((BYTE *)(*Value)) = 0;
                }
                break;
*/
            case REG_SZ:
            case REG_EXPAND_SZ:
            case REG_MULTI_SZ:
                if ( *Value == NULL ) {
                    *Value = (PVOID)ScepAlloc( LMEM_ZEROINIT, (dSize+1)*sizeof(TCHAR));
                    FreeMem = TRUE;
                }

                if ( *Value == NULL ) {
                    Rcode = ERROR_NOT_ENOUGH_MEMORY;
                } else {
                    Rcode = RegQueryValueEx(hKey,
                                           ValueName,
                                           0,
                                           pRegType,
                                           (BYTE *)(*Value),
                                           &dSize
                                          );
                    if ( Rcode != ERROR_SUCCESS && FreeMem ) {
                        ScepFree(*Value);
                        *Value = NULL;
                    }
                }

                break;
            default:

                Rcode = ERROR_INVALID_DATATYPE;

                break;
            }
        }
    }

    if( hKey )
        RegCloseKey( hKey );

    return(Rcode);
}

DWORD
ScepRegDeleteValue(
    IN HKEY hKeyRoot,
    IN PWSTR SubKey,
    IN PWSTR ValueName
   )
/* ++

Routine Description:

   This routine delete the reg value

Arguments:

   hKeyRoot    - root

   SubKey      - key path

   ValueName   - name of the value


Return values:

   Win32 error code
-- */
{
    DWORD   Rcode;
    HKEY    hKey=NULL;

    if(( Rcode = RegOpenKeyEx(hKeyRoot,
                              SubKey,
                              0,
                              KEY_READ | KEY_WRITE,
                              &hKey
                             )) == ERROR_SUCCESS ) {

        Rcode = RegDeleteValue(hKey, ValueName);

    }

    if( hKey )
        RegCloseKey( hKey );

    return(Rcode);
}


SCESTATUS
ScepCreateDirectory(
    IN PCWSTR ProfileLocation,
    IN BOOL FileOrDir,
    PSECURITY_DESCRIPTOR pSecurityDescriptor OPTIONAL
    )
/* ++
Routine Description:

    This routine creates directory(ies) as specified in the ProfileLocation.


Arguments:

    ProfileLocation     - The directory (full path) to create

    FileOrDir           - TRUE = Dir name, FALSE = file name

    pSecurityDescriptor - The secrity descriptor for the directories to create.
                            If it is NULL, then the parent directory's inherit
                            security descriptor is used.

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_NOT_ENOUGH_RESOURCE
    SCESTATUS_OTHER_ERROR

-- */
{
    PWSTR       Buffer=NULL;
    PWSTR       pTemp=NULL;
    DWORD       Len=0;
    SCESTATUS    rc;
    SECURITY_ATTRIBUTES sa;


    if ( ProfileLocation == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( wcsncmp(ProfileLocation,L"\\\\?\\",4) == 0 ) {

        pTemp = (PWSTR)ProfileLocation+4;
    } else {
        pTemp = (PWSTR)ProfileLocation;
    }

    //
    // skip the first '\\' for example, c:\winnt
    //
    pTemp = wcschr(pTemp, L'\\');
    if ( pTemp == NULL ) {
        if ( ProfileLocation[1] == L':' ) {
            return(SCESTATUS_SUCCESS);
        } else {
            return(SCESTATUS_INVALID_PARAMETER);
        }
    } else if ( *(pTemp+1) == L'\\' ) {
        //
        // there is a machine name here
        //
        pTemp = wcschr(pTemp+2, L'\\');
        if ( pTemp == NULL ) {
            //
            // just a machine name, invalid
            //
            return(SCESTATUS_INVALID_PARAMETER);
        } else {
            //
            // look for the share name end
            //
            pTemp = wcschr(pTemp+1, L'\\');

            if ( pTemp == NULL ) {
                //
                // no directory is specified
                //
                return(SCESTATUS_INVALID_PARAMETER);
            }

        }

    }

    //
    // Make a copy of the profile location
    //
    Buffer = (PWSTR)ScepAlloc( 0, (wcslen(ProfileLocation)+1)*sizeof(WCHAR));
    if ( Buffer == NULL ) {
        return(SCESTATUS_NOT_ENOUGH_RESOURCE);
    }

    wcscpy( Buffer, ProfileLocation );

    //
    // Looping to find the next '\\'
    //
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = (LPVOID)pSecurityDescriptor;
    sa.bInheritHandle = FALSE;

    do {
        pTemp = wcschr( pTemp+1, L'\\');
        if ( pTemp != NULL ) {
            Len = (DWORD)(pTemp - ProfileLocation);
            Buffer[Len] = L'\0';
        } else if ( FileOrDir )
            Len = 0;  // dir name
        else
            break;    // file name. DO NOT create directory for the file name part

        //
        // should make a security descriptor and set
        //
        if ( CreateDirectory(
                Buffer,
                &sa
                ) == FALSE ) {
            if ( GetLastError() != ERROR_ALREADY_EXISTS ) {
                rc = ScepDosErrorToSceStatus(GetLastError());
                goto Done;
            }
        }

        if ( Len != 0 )
            Buffer[Len] = L'\\';


    } while (  pTemp != NULL );

    rc = SCESTATUS_SUCCESS;

Done:

    ScepFree(Buffer);

    return(rc);

}



DWORD
ScepSceStatusToDosError(
    IN SCESTATUS SceStatus
    )
// converts SCESTATUS error code to dos error defined in winerror.h
{
    switch(SceStatus) {

    case SCESTATUS_SUCCESS:
        return(NO_ERROR);

    case SCESTATUS_OTHER_ERROR:
        return(ERROR_EXTENDED_ERROR);

    case SCESTATUS_INVALID_PARAMETER:
        return(ERROR_INVALID_PARAMETER);

    case SCESTATUS_RECORD_NOT_FOUND:
        return(ERROR_NO_MORE_ITEMS);

    case SCESTATUS_NO_MAPPING:
        return(ERROR_NONE_MAPPED);

    case SCESTATUS_TRUST_FAIL:
        return(ERROR_TRUSTED_DOMAIN_FAILURE);

    case SCESTATUS_INVALID_DATA:
        return(ERROR_INVALID_DATA);

    case SCESTATUS_OBJECT_EXIST:
        return(ERROR_FILE_EXISTS);

    case SCESTATUS_BUFFER_TOO_SMALL:
        return(ERROR_INSUFFICIENT_BUFFER);

    case SCESTATUS_PROFILE_NOT_FOUND:
        return(ERROR_FILE_NOT_FOUND);

    case SCESTATUS_BAD_FORMAT:
        return(ERROR_BAD_FORMAT);

    case SCESTATUS_NOT_ENOUGH_RESOURCE:
        return(ERROR_NOT_ENOUGH_MEMORY);

    case SCESTATUS_ACCESS_DENIED:
        return(ERROR_ACCESS_DENIED);

    case SCESTATUS_CANT_DELETE:
        return(ERROR_CURRENT_DIRECTORY);

    case SCESTATUS_PREFIX_OVERFLOW:
        return(ERROR_BUFFER_OVERFLOW);

    case SCESTATUS_ALREADY_RUNNING:
        return(ERROR_SERVICE_ALREADY_RUNNING);

    case SCESTATUS_SERVICE_NOT_SUPPORT:
        return(ERROR_NOT_SUPPORTED);

    case SCESTATUS_MOD_NOT_FOUND:
        return(ERROR_MOD_NOT_FOUND);

    case SCESTATUS_EXCEPTION_IN_SERVER:
        return(ERROR_EXCEPTION_IN_SERVICE);

    case SCESTATUS_JET_DATABASE_ERROR:
        return(ERROR_DATABASE_FAILURE);

    case SCESTATUS_TIMEOUT:
        return(ERROR_TIMEOUT);

    case SCESTATUS_PENDING_IGNORE:
        return(ERROR_IO_PENDING);

    case SCESTATUS_SPECIAL_ACCOUNT:
        return(ERROR_SPECIAL_ACCOUNT);

    default:
        return(ERROR_EXTENDED_ERROR);
    }
}



SCESTATUS
ScepDosErrorToSceStatus(
    DWORD rc
    )
{
    switch(rc) {
    case NO_ERROR:
        return(SCESTATUS_SUCCESS);

    case ERROR_INVALID_PARAMETER:
    case RPC_S_INVALID_STRING_BINDING:
    case RPC_S_INVALID_BINDING:
    case RPC_X_NULL_REF_POINTER:
        return(SCESTATUS_INVALID_PARAMETER);

    case ERROR_INVALID_DATA:
        return(SCESTATUS_INVALID_DATA);

    case ERROR_FILE_NOT_FOUND:
    case ERROR_PATH_NOT_FOUND:
    case ERROR_BAD_NETPATH:

        return(SCESTATUS_PROFILE_NOT_FOUND);

    case ERROR_ACCESS_DENIED:
    case ERROR_SHARING_VIOLATION:
    case ERROR_LOCK_VIOLATION:
    case ERROR_NETWORK_ACCESS_DENIED:
    case ERROR_CANT_ACCESS_FILE:
    case RPC_S_SERVER_TOO_BUSY:

        return(SCESTATUS_ACCESS_DENIED);

    case ERROR_NOT_ENOUGH_MEMORY:
    case ERROR_OUTOFMEMORY:
    case RPC_S_OUT_OF_RESOURCES:
        return(SCESTATUS_NOT_ENOUGH_RESOURCE);

    case ERROR_BAD_FORMAT:
        return(SCESTATUS_BAD_FORMAT);

    case ERROR_CURRENT_DIRECTORY:
        return(SCESTATUS_CANT_DELETE);

    case ERROR_SECTOR_NOT_FOUND:
    case ERROR_SERVICE_DOES_NOT_EXIST:
    case ERROR_RESOURCE_DATA_NOT_FOUND:
    case ERROR_NO_MORE_ITEMS:
        return(SCESTATUS_RECORD_NOT_FOUND);

    case ERROR_NO_TRUST_LSA_SECRET:
    case ERROR_NO_TRUST_SAM_ACCOUNT:
    case ERROR_TRUSTED_DOMAIN_FAILURE:
    case ERROR_TRUSTED_RELATIONSHIP_FAILURE:
    case ERROR_TRUST_FAILURE:
        return(SCESTATUS_TRUST_FAIL);

    case ERROR_NONE_MAPPED:
        return(SCESTATUS_NO_MAPPING);

    case ERROR_DUP_NAME:
    case ERROR_FILE_EXISTS:

        return(SCESTATUS_OBJECT_EXIST);

    case ERROR_BUFFER_OVERFLOW:
        return(SCESTATUS_PREFIX_OVERFLOW);

    case ERROR_INSUFFICIENT_BUFFER:
    case RPC_S_STRING_TOO_LONG:

        return(SCESTATUS_BUFFER_TOO_SMALL);

    case ERROR_SERVICE_ALREADY_RUNNING:
        return(SCESTATUS_ALREADY_RUNNING);

    case ERROR_NOT_SUPPORTED:
    case RPC_S_INVALID_NET_ADDR:
    case RPC_S_NO_ENDPOINT_FOUND:
    case RPC_S_SERVER_UNAVAILABLE:
    case RPC_S_CANNOT_SUPPORT:
        return(SCESTATUS_SERVICE_NOT_SUPPORT);

    case ERROR_MOD_NOT_FOUND:
    case ERROR_PROC_NOT_FOUND:
        return(SCESTATUS_MOD_NOT_FOUND);

    case ERROR_EXCEPTION_IN_SERVICE:
        return(SCESTATUS_EXCEPTION_IN_SERVER);

    case ERROR_DATABASE_FAILURE:
        return(SCESTATUS_JET_DATABASE_ERROR);

    case ERROR_TIMEOUT:
        return(SCESTATUS_TIMEOUT);

    case ERROR_IO_PENDING:
        return(SCESTATUS_PENDING_IGNORE);

    case ERROR_SPECIAL_ACCOUNT:
    case ERROR_PASSWORD_RESTRICTION:
        return(SCESTATUS_SPECIAL_ACCOUNT);

    default:
        return(SCESTATUS_OTHER_ERROR);

    }
}


SCESTATUS
ScepChangeAclRevision(
    IN PSECURITY_DESCRIPTOR pSD,
    IN BYTE NewRevision
    )
/*
Change AclRevision to the NewRevision.

This routine is made for backward compatility because NT4 does not support
the new ACL_REVISION_DS.

*/
{
    BOOLEAN bPresent=FALSE;
    BOOLEAN bDefault=FALSE;
    PACL    pAcl=NULL;
    NTSTATUS    NtStatus;


    if ( pSD ) {
        //
        // change acl revision on DACL
        //
        NtStatus = RtlGetDaclSecurityDescriptor (
                        pSD,
                        &bPresent,
                        &pAcl,
                        &bDefault
                        );
        if ( NT_SUCCESS(NtStatus) && bPresent && pAcl ) {
            pAcl->AclRevision = NewRevision;
        }

        //
        // change acl revision on SACL
        //
        pAcl = NULL;
        bPresent = FALSE;

        NtStatus = RtlGetSaclSecurityDescriptor (
                        pSD,
                        &bPresent,
                        &pAcl,
                        &bDefault
                        );
        if ( NT_SUCCESS(NtStatus) && bPresent && pAcl ) {
            pAcl->AclRevision = NewRevision;
        }

    }
    return(SCESTATUS_SUCCESS);

}

BOOL
ScepEqualSid(
    IN PISID pSid1,
    IN PISID pSid2
    )
{
    if ( pSid1 == NULL && pSid2 == NULL )
        return(TRUE);
    if ( pSid1 == NULL || pSid2 == NULL )
        return(FALSE);

    return (EqualSid((PSID)pSid1, (PSID)pSid2) ? TRUE: FALSE);
}

BOOL
ScepEqualGuid(
    IN GUID *Guid1,
    IN GUID *Guid2
    )
{
    if ( Guid1 == NULL && Guid2 == NULL )
        return(TRUE);
    if ( Guid1 == NULL || Guid2 == NULL )
        return(FALSE);
/*
    if ( Guid1->Data1 != Guid2->Data1 ||
         Guid1->Data2 != Guid2->Data2 ||
         Guid1->Data3 != Guid2->Data3 ||
         *((DWORD *)(Guid1->Data4)) != *((DWORD *)(Guid2->Data4)) ||
         *((DWORD *)(Guid1->Data4)+1) != *((DWORD *)(Guid2->Data4)+1) )
        return(FALSE);

    return(TRUE);
*/
    return (!memcmp(Guid1, Guid2, sizeof(GUID)));
}


SCESTATUS
ScepAddToGroupMembership(
    OUT PSCE_GROUP_MEMBERSHIP *pGroupMembership,
    IN  PWSTR Keyname,
    IN  DWORD KeyLen,
    IN  PSCE_NAME_LIST pMembers,
    IN  DWORD ValueType,
    IN  BOOL bCheckDup,
    IN  BOOL bReplaceList
    )
{
    PSCE_GROUP_MEMBERSHIP   pGroup=NULL;
    SCESTATUS rc=SCESTATUS_SUCCESS;

    if ( pGroupMembership == NULL || Keyname == NULL ||
         KeyLen <= 0 ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    //
    // find if the group is already defined
    //
    if ( bCheckDup ) {

        for ( pGroup=*pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {
            if ( _wcsnicmp( pGroup->GroupName, Keyname, KeyLen) == 0 &&
                 pGroup->GroupName[KeyLen] == L'\0' )
                break;
        }
    }

    if ( pGroup == NULL ) {
        // not found. create a new node

        pGroup = (PSCE_GROUP_MEMBERSHIP)ScepAlloc( LMEM_ZEROINIT,
                                                 sizeof(SCE_GROUP_MEMBERSHIP) );

        if ( pGroup != NULL ) {

            pGroup->GroupName = (PWSTR)ScepAlloc( LMEM_ZEROINIT,
                                                 (KeyLen+1)*sizeof(WCHAR) );
            if (pGroup->GroupName != NULL) {

                wcsncpy( pGroup->GroupName, Keyname, KeyLen );

                pGroup->Next = *pGroupMembership;
                pGroup->Status = SCE_GROUP_STATUS_NC_MEMBERS | SCE_GROUP_STATUS_NC_MEMBEROF;
                *pGroupMembership = pGroup;

            } else {

                ScepFree(pGroup);
                rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
            }

        } else
            rc  = SCESTATUS_NOT_ENOUGH_RESOURCE;

    }

    if ( rc == SCESTATUS_SUCCESS ) {

        if ( ValueType == 0 ) {

            if ( bReplaceList ) {

                ScepFreeNameList(pGroup->pMembers);
                pGroup->pMembers = pMembers;

            } else if ( pGroup->pMembers == NULL )
                pGroup->pMembers = pMembers;

            pGroup->Status &= ~SCE_GROUP_STATUS_NC_MEMBERS;

        } else {

            if ( bReplaceList ) {

                ScepFreeNameList(pGroup->pMemberOf);
                pGroup->pMemberOf = pMembers;

            } else if ( pGroup->pMemberOf == NULL )
                pGroup->pMemberOf = pMembers;

            pGroup->Status &= ~SCE_GROUP_STATUS_NC_MEMBEROF;
        }
    }

    return(rc);

}


DWORD
ScepAddOneServiceToList(
    IN LPWSTR lpServiceName,
    IN LPWSTR lpDisplayName,
    IN DWORD ServiceStatus,
    IN PVOID pGeneral OPTIONAL,
    IN SECURITY_INFORMATION SeInfo,
    IN BOOL bSecurity,
    OUT PSCE_SERVICES *pServiceList
    )
/*
Routine Description:

    Add service name, startup status, security descriptor or a engine name
    to the service list. The service list must be freed using SceFreePSCE_SERVICES

Arguments:

    lpServiceName - The service name

    ServiceStatus - The startup status of the service

    pGeneral - The security descriptor or the engine dll name, decided by
                bSecurity

    bSecurity - TRUE = a security descriptor is passed in pGeneral
                FALSE = a engine dll name is passed in pGeneral

    pServiceList - The service list to output

Return Value:

    ERROR_SUCCESS
    Win32 errors
*/
{
    if ( NULL == lpServiceName || pServiceList == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    PSCE_SERVICES pServiceNode;
    DWORD rc=ERROR_SUCCESS;

    //
    // allocate a service node
    //
    pServiceNode = (PSCE_SERVICES)LocalAlloc(LMEM_ZEROINIT, sizeof(SCE_SERVICES));
    if ( pServiceNode != NULL ) {
       //
       // allocate buffer for ServiceName
       //
       pServiceNode->ServiceName = (PWSTR)LocalAlloc(LMEM_FIXED,
                                    (wcslen(lpServiceName) + 1)*sizeof(WCHAR));
       if ( NULL != pServiceNode->ServiceName ) {
           //
           // fill the service node
           //
           if ( lpDisplayName != NULL ) {

               pServiceNode->DisplayName = (PWSTR)LocalAlloc(LMEM_FIXED,
                                    (wcslen(lpDisplayName) + 1)*sizeof(WCHAR));

               if ( pServiceNode->DisplayName != NULL ) {
                   wcscpy(pServiceNode->DisplayName, lpDisplayName);

               } else {
                   rc = ERROR_NOT_ENOUGH_MEMORY;
               }
           } else
               pServiceNode->DisplayName = NULL;

           if ( rc == NO_ERROR ) {

               wcscpy(pServiceNode->ServiceName, lpServiceName);
               pServiceNode->Status = 0;
               pServiceNode->Startup = (BYTE)ServiceStatus;

               if ( bSecurity ) {
                   //
                   // security descriptor
                   //
                   pServiceNode->General.pSecurityDescriptor = (PSECURITY_DESCRIPTOR)pGeneral;
                   pServiceNode->SeInfo = SeInfo;
               } else {
                   //
                   // service engine name
                   //
                   pServiceNode->General.ServiceEngineName = (PWSTR)pGeneral;
               }
               //
               // link to the list
               //
               pServiceNode->Next = *pServiceList;
               *pServiceList = pServiceNode;

           } else {
               LocalFree(pServiceNode->ServiceName);
           }

       } else
           rc = ERROR_NOT_ENOUGH_MEMORY;

       if ( rc != ERROR_SUCCESS ) {
           LocalFree(pServiceNode);
       }

    } else
        rc = ERROR_NOT_ENOUGH_MEMORY;

    return(rc);
}



DWORD
ScepIsAdminLoggedOn(
    OUT PBOOL bpAdminLogon
    )
{

    HANDLE          Token;
    NTSTATUS        NtStatus;
    SID_IDENTIFIER_AUTHORITY IdAuth=SECURITY_NT_AUTHORITY;
    PSID            AdminsSid=NULL;


    if ( bpAdminLogon == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    *bpAdminLogon = FALSE;

    if (!OpenThreadToken( GetCurrentThread(),
                          TOKEN_QUERY | TOKEN_DUPLICATE,
                          FALSE,
                          &Token)) {

        if (!OpenProcessToken( GetCurrentProcess(),
                               TOKEN_QUERY | TOKEN_DUPLICATE,
                               &Token))

            return(GetLastError());

    }
    //
    // Parepare AdminsSid
    //
    NtStatus = RtlAllocateAndInitializeSid(
                    &IdAuth,
                    2,
                    SECURITY_BUILTIN_DOMAIN_RID,
                    DOMAIN_ALIAS_RID_ADMINS,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    &AdminsSid );

    DWORD rc32 = RtlNtStatusToDosError(NtStatus);

    if (NT_SUCCESS(NtStatus) ) {

        //
        // use the CheckTokenMembership API which handles the group attributes
        //

        HANDLE NewToken;

        if ( DuplicateToken( Token, SecurityImpersonation, &NewToken )) {

            if ( FALSE == CheckTokenMembership(
                                NewToken,
                                AdminsSid,
                                bpAdminLogon
                                ) ) {

                //
                // error occured when checking membership, assume it is not a member
                //

                *bpAdminLogon = FALSE;

                rc32 = GetLastError();

            }

            CloseHandle(NewToken);

        } else {

            rc32 = GetLastError();
        }

#if 0
        DWORD           i;
        DWORD           ReturnLen, NewLen;
        PVOID           Info=NULL;

        //
        // check groups
        //
        NtStatus = NtQueryInformationToken (
                        Token,
                        TokenGroups,
                        NULL,
                        0,
                        &ReturnLen
                        );
        if ( NtStatus == STATUS_BUFFER_TOO_SMALL ) {
            //
            // allocate buffer
            //
            Info = ScepAlloc(0, ReturnLen+1);

            if ( Info != NULL ) {
                NtStatus = NtQueryInformationToken (
                                Token,
                                TokenGroups,
                                Info,
                                ReturnLen,
                                &NewLen
                                );
                if ( NT_SUCCESS(NtStatus) ) {

                    for ( i = 0; i<((PTOKEN_GROUPS)Info)->GroupCount; i++) {
                        //
                        // check each group sid
                        //
                        if ( ((PTOKEN_GROUPS)Info)->Groups[i].Sid != NULL &&
                             RtlEqualSid(((PTOKEN_GROUPS)Info)->Groups[i].Sid, AdminsSid) ) {
                            *bpAdminLogon = TRUE;
                            break;
                        }
                    }
                }

                ScepFree(Info);
            }
        }

        rc32 = RtlNtStatusToDosError(NtStatus);

#endif

        //
        // Free administrators Sid
        //
        RtlFreeSid(AdminsSid);
    }

    CloseHandle(Token);

    return(rc32);

}


DWORD
ScepGetProfileSetting(
    IN PCWSTR ValueName,
    IN BOOL bAdminLogon,
    OUT PWSTR *Setting
    )
/*
Routine Description:

    This routine returns JET profile setting for the ValueName from registry.
    If there is no setting in registry (e.g., first time), a default setting
    for the ValueName will be built. The output Setting string must be freed
    by LocalFree after its use.

Arguments:

    ValueName - The registry value name to retrieve

    bAdminLogon - The flag to indicate if logged on user is admin equivalent

    Setting - the ouptut buffer

Return Value:

    Win32 error codes
*/
{
    DWORD RegType;
    DWORD rc;
    PWSTR SysRoot=NULL;
    PWSTR ProfilePath=NULL;
    TCHAR TempName[256];


    if ( ValueName == NULL || Setting == NULL ) {
        return( ERROR_INVALID_PARAMETER );
    }

    *Setting = NULL;

    if (bAdminLogon ) {
        if ( _wcsicmp(L"DefaultProfile", ValueName ) == 0 ) {
            //
            // do not query the system database name in registry
            //
            rc = ERROR_FILE_NOT_FOUND;

        } else {

            rc = ScepRegQueryValue(
                    HKEY_LOCAL_MACHINE,
                    SCE_ROOT_PATH,
                    ValueName,
                    (PVOID *)Setting,
                    &RegType
                    );
        }

    } else {

        HKEY hCurrentUser=NULL;
        //
        // the HKEY_CURRENT_USER may be linked to .default
        // depends on the current calling process
        //
        rc =RegOpenCurrentUser(
                KEY_READ,
                &hCurrentUser
                );

        if ( rc != NO_ERROR ) {
            hCurrentUser = NULL;
        }

        rc = ScepRegQueryValue(
                hCurrentUser ? hCurrentUser : HKEY_CURRENT_USER,
                SCE_ROOT_PATH,
                ValueName,
                (PVOID *)Setting,
                &RegType
                );

        if ( hCurrentUser ) {
            // close it
            RegCloseKey(hCurrentUser);
        }
    }

    //
    // if registry type is not REG_SZ or REG_EXPAND_SZ,
    // return status won't be SUCCESS
    //

    if ( rc != NO_ERROR ) {

        //
        // use the default
        //
        RegType =  0;
        rc = ScepGetNTDirectory( &SysRoot, &RegType, SCE_FLAG_WINDOWS_DIR );

        if ( rc == NO_ERROR ) {

            if ( SysRoot != NULL ) {

                if ( bAdminLogon ) {
                    //
                    // default location is %SystemRoot%\Security\Database\secedit.sdb
                    //
                    wcscpy(TempName, L"\\Security\\Database\\secedit.sdb");
                    RegType += wcslen(TempName)+1;

                    *Setting = (PWSTR)ScepAlloc( 0, RegType*sizeof(WCHAR));
                    if ( *Setting != NULL ) {
                        swprintf(*Setting, L"%s%s", SysRoot, TempName );

                        *(*Setting+RegType-1) = L'\0';
/*
                        // do not save system database name
                        // set this value as the default profile name for administrators
                        //

                        ScepRegSetValue(
                            HKEY_LOCAL_MACHINE,
                            SCE_ROOT_PATH,
                            (PWSTR)ValueName,
                            REG_SZ,
                            (BYTE *)(*Setting),
                            (RegType-1)*sizeof(WCHAR)
                            );
*/

                    } else
                        rc = ERROR_NOT_ENOUGH_MEMORY;

                } else {
                    //
                    // default location is <UserProfilesDirectory>\Profiles\<User>\secedit.sdb
                    // on NT5, it will be %SystemDrive%\Users\Profiles...
                    // on NT4, it is %SystemRoot%\Profiles...
                    // GetCurrentUserProfilePath already handles NT4/NT5 difference
                    //
                    rc = ScepGetCurrentUserProfilePath(&ProfilePath);

                    if ( rc == NO_ERROR && ProfilePath != NULL ) {
                        //
                        // get the current user profile path
                        //
                        wcscpy(TempName, L"\\secedit.sdb");

                        *Setting = (PWSTR)ScepAlloc(0, (wcslen(ProfilePath)+wcslen(TempName)+1)*sizeof(WCHAR));

                        if ( *Setting != NULL ) {
                            swprintf(*Setting, L"%s%s\0", ProfilePath,TempName );

                        } else
                            rc = ERROR_NOT_ENOUGH_MEMORY;

                        ScepFree(ProfilePath);

                    } else {

                        rc = NO_ERROR;
                        wcscpy(TempName, L"\\Profiles\\secedit.sdb");
#if _WINNT_WIN32>=0x0500
                        //
                        // default to <ProfilesDirectory>\Profiles
                        // get the profiles directory first
                        //
                        RegType = 0;
                        GetProfilesDirectory(NULL, &RegType);

                        if ( RegType ) {
                            //
                            // allocate the total buffer
                            //
                            RegType += wcslen(TempName);

                            *Setting = (PWSTR)ScepAlloc( LMEM_ZEROINIT, (RegType+1)*sizeof(WCHAR));

                            if ( *Setting ) {
                                //
                                // call to get the profiles directory again
                                //
                                if ( GetProfilesDirectory(*Setting, &RegType) ) {

                                    wcscat(*Setting, TempName );

                                } else {
                                    rc = GetLastError();

                                    ScepFree(*Setting);
                                    *Setting = NULL;
                                }

                            } else {
                                rc = ERROR_NOT_ENOUGH_MEMORY;
                            }

                        } else {
                            rc = GetLastError();
                        }
#else
                        //
                        // default to %SystemRoot%\Profiles
                        //
                        RegType += wcslen(TempName)+1;

                        *Setting = (PWSTR)ScepAlloc( 0, RegType*sizeof(WCHAR));

                        if ( *Setting != NULL ) {
                            swprintf(*Setting, L"%s%s", SysRoot,TempName );

                            *(*Setting+RegType-1) = L'\0';

                            rc = ERROR_SUCCESS;

                        } else {
                            rc = ERROR_NOT_ENOUGH_MEMORY;
                        }
#endif
                    }
                }

                ScepFree(SysRoot);

            } else
                rc = ERROR_INVALID_DATA;
        }
    }

    return(rc);
}



DWORD
ScepCompareObjectSecurity(
    IN SE_OBJECT_TYPE ObjectType,
    IN BOOL IsContainer,
    IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
    IN PSECURITY_DESCRIPTOR ProfileSD,
    IN SECURITY_INFORMATION ProfileSeInfo,
    OUT PBYTE IsDifferent
    )
/* ++

Routine Description:

   Compare two security descriptors

Arguments:

   ObjectType         - the object type

   pSecurityDescriptor - The security descriptor of current object's setting

   ProfileSD          - security descriptor specified in the template

   ProfileSeInfo      - security information specified in the template

Return value:

   SCESTATUS error codes

++ */
{
    BOOL    Different=FALSE;
    BOOL    DifPermOrAudit;
    DWORD   rc=ERROR_SUCCESS;
    PSID    pSid1=NULL;
    PSID    pSid2=NULL;
    BOOLEAN tFlag;
    BOOLEAN aclPresent;
    PACL    pAcl1=NULL;
    PACL    pAcl2=NULL;
    SECURITY_DESCRIPTOR_CONTROL Control1;
    SECURITY_DESCRIPTOR_CONTROL Control2;
    DWORD       Win32rc;


    BYTE   Status=0;

    if ( IsDifferent == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( pSecurityDescriptor == NULL &&
         ProfileSD == NULL ) {

        if ( IsDifferent ) {
            *IsDifferent = SCE_STATUS_MISMATCH;
        }
        return(rc);
    }

    if ( IsDifferent ) {
        *IsDifferent = 0;
    }

    //
    // if ProfileSD is specified and protection does not match SystemSD, then mismatch
    // don't care if ProfileSD is not specified
    //

    if ( pSecurityDescriptor == NULL || !NT_SUCCESS(RtlGetControlSecurityDescriptor (
                                                                                    pSecurityDescriptor,
                                                                                    &Control1,
                                                                                    &Win32rc  // temp use
                                                                                    ))) {

        Control1 = 0;

    }

    if ( ProfileSD == NULL || !NT_SUCCESS(RtlGetControlSecurityDescriptor (
                                                                          ProfileSD,
                                                                          &Control2,
                                                                          &Win32rc  // temp use
                                                                          ))) {

        Control2 = 0;

    }

    if ((Control1 & SE_DACL_PROTECTED) != (Control2 & SE_DACL_PROTECTED)) {

        Different = TRUE;
        Status |= SCE_STATUS_PERMISSION_MISMATCH;

    }

    if ((Control1 & SE_SACL_PROTECTED) != (Control2 & SE_SACL_PROTECTED)) {

        Different = TRUE;
        Status |= SCE_STATUS_AUDIT_MISMATCH;

    }


    //
    // Compare two security descriptors
    //
    if ( ProfileSeInfo & OWNER_SECURITY_INFORMATION ) {
        if ( pSecurityDescriptor == NULL ||
             !NT_SUCCESS( RtlGetOwnerSecurityDescriptor(
                                     pSecurityDescriptor,
                                     &pSid1,
                                     &tFlag)
                                    ) ) {

            pSid1 = NULL;
        }
        if ( ProfileSD == NULL ||
             !NT_SUCCESS( RtlGetOwnerSecurityDescriptor(
                                         ProfileSD,
                                         &pSid2,
                                         &tFlag)
                                        ) ) {

            pSid2 = NULL;
        }
        if ( (pSid1 == NULL && pSid2 != NULL) ||
             (pSid1 != NULL && pSid2 == NULL) ||
             (pSid1 != NULL && pSid2 != NULL && !EqualSid(pSid1, pSid2)) ) {

            Different = TRUE;
        }
    }

#if 0
    //
    // Get Group address
    //

    if ( ProfileSeInfo & GROUP_SECURITY_INFORMATION ) {
        pSid1 = NULL;
        pSid2 = NULL;
        if ( pSecurityDescriptor == NULL ||
             !NT_SUCCESS( RtlGetGroupSecurityDescriptor(
                                  pSecurityDescriptor,
                                  &pSid1,
                                  &tFlag)
                                ) ) {

            pSid1 = NULL;
        }
        if ( ProfileSD == NULL ||
             !NT_SUCCESS( RtlGetGroupSecurityDescriptor(
                                          ProfileSD,
                                          &pSid2,
                                          &tFlag)
                                        ) ) {

            pSid2 = NULL;
        }
        if ( (pSid1 == NULL && pSid2 != NULL) ||
             (pSid1 != NULL && pSid2 == NULL) ||
             (pSid1 != NULL && pSid2 != NULL && !EqualSid(pSid1, pSid2)) ) {

            Different = TRUE;
        }
    }
#endif

    //
    // Get DACL address
    //

    if ( !(Status & SCE_STATUS_PERMISSION_MISMATCH) && (ProfileSeInfo & DACL_SECURITY_INFORMATION) ) {
        if ( pSecurityDescriptor == NULL ||
             !NT_SUCCESS( RtlGetDaclSecurityDescriptor(
                                         pSecurityDescriptor,
                                         &aclPresent,
                                         &pAcl1,
                                         &tFlag)
                                       ) ) {

            pAcl1 = NULL;
        } else if ( !aclPresent )
            pAcl1 = NULL;
        if ( ProfileSD == NULL ||
             !NT_SUCCESS( RtlGetDaclSecurityDescriptor(
                                         ProfileSD,
                                         &aclPresent,
                                         &pAcl2,
                                         &tFlag)
                                       ) ) {

            pAcl2 = NULL;
        } else if ( !aclPresent )
            pAcl2 = NULL;

        //
        // compare two ACLs
        //
        DifPermOrAudit = FALSE;
        rc = ScepCompareExplicitAcl( ObjectType, IsContainer, pAcl1, pAcl2, &DifPermOrAudit );

        if ( rc != ERROR_SUCCESS ) {
            goto Done;
        }

        if ( DifPermOrAudit ) {
            Different = TRUE;
            Status |= SCE_STATUS_PERMISSION_MISMATCH;
        }
    }

    //
    // Get SACL address
    //

    if ( !(Status & SCE_STATUS_AUDIT_MISMATCH) && (ProfileSeInfo & SACL_SECURITY_INFORMATION) ) {
        pAcl1 = NULL;
        pAcl2 = NULL;
        if ( pSecurityDescriptor == NULL ||
             !NT_SUCCESS( RtlGetSaclSecurityDescriptor(
                                         pSecurityDescriptor,
                                         &aclPresent,
                                         &pAcl1,
                                         &tFlag)
                                       ) ) {

            pAcl1 = NULL;

        } else if ( !aclPresent )
            pAcl1 = NULL;

        if ( ProfileSD == NULL ||
             !NT_SUCCESS( RtlGetSaclSecurityDescriptor(
                                         ProfileSD,
                                         &aclPresent,
                                         &pAcl2,
                                         &tFlag)
                                       ) ) {

            pAcl2 = NULL;

        } else if ( !aclPresent )
            pAcl2 = NULL;

        //
        // compare two ACLs
        //
        DifPermOrAudit = FALSE;
        rc = ScepCompareExplicitAcl( ObjectType, IsContainer, pAcl1, pAcl2, &DifPermOrAudit );

        if ( rc != ERROR_SUCCESS ) {
            goto Done;
        }

        if ( DifPermOrAudit ) {
            Different = TRUE;
            Status |= SCE_STATUS_AUDIT_MISMATCH;
        }
    }

    if ( IsDifferent && Different ) {

        *IsDifferent = SCE_STATUS_MISMATCH;

        if ( Status ) {
            *IsDifferent |= Status;
        }
    }

Done:

    return(rc);
}



DWORD
ScepCompareExplicitAcl(
    IN SE_OBJECT_TYPE ObjectType,
    IN BOOL IsContainer,
    IN PACL pAcl1,
    IN PACL pAcl2,
    OUT PBOOL pDifferent
    )
/*
Routine Description:

    This routine compares explicit aces of two ACLs for exact match. Exact
    match means: same access type, same inheritance flag, same access mask,
    same GUID/Object GUID (if available), and same SID.

    Inherited aces (INHERITED_ACE is set) are ignored.

Arguments:

    pAcl1 - The first ACL

    pAcl2 - The 2nd ACL

    pDifferent - The output flag to indicate different

Return Value:

    Win32 error codes
*/
{
    NTSTATUS        NtStatus=STATUS_SUCCESS;
    DWORD           dwAcl1AceCount, dwAcl2AceCount;
    ACE_HEADER      *pAce1=NULL;
    ACE_HEADER      *pAce2=NULL;
    PSCEP_ADL_NODE hTable1 [SCEP_ADL_HTABLE_SIZE];
    PSCEP_ADL_NODE hTable2 [SCEP_ADL_HTABLE_SIZE];

    memset(hTable1, NULL, SCEP_ADL_HTABLE_SIZE * sizeof(PSCEP_ADL_NODE) );
    memset(hTable2, NULL, SCEP_ADL_HTABLE_SIZE * sizeof(PSCEP_ADL_NODE) );

    *pDifferent = FALSE;

    //
    // if pAcl1 is NULL, pAcl2 should have 0 explicit Ace
    //
    if ( pAcl1 == NULL ) {
        NtStatus = ScepAnyExplicitAcl( pAcl2, 0, pDifferent );
        return(RtlNtStatusToDosError(NtStatus));
    }

    //
    // if pAcl2 is NULL, pAcl1 should have 0 explicit Ace
    //
    if ( pAcl2 == NULL ) {
        NtStatus = ScepAnyExplicitAcl( pAcl1, 0, pDifferent );
        return(RtlNtStatusToDosError(NtStatus));
    }

    //
    // both ACLs are not NULL
    //

    BOOL bAcl1NoExplicitAces;
    BOOL bAcl2NoExplicitAces;

    dwAcl1AceCount = 0;
    dwAcl2AceCount = 0;

    while ( dwAcl1AceCount < pAcl1->AceCount || dwAcl2AceCount < pAcl2->AceCount) {
        //
        // convert Acl1 into Access Description Language and insert into htable for this blob
        // blob is defined as a contiguous AceList of same type
        //
        bAcl1NoExplicitAces = TRUE;
        if (dwAcl1AceCount < pAcl1->AceCount) {
            NtStatus = ScepConvertAclBlobToAdl(ObjectType,
                                               IsContainer,
                                               pAcl1,
                                               &dwAcl1AceCount,
                                               &bAcl1NoExplicitAces,
                                               hTable1);

                if ( !NT_SUCCESS(NtStatus) )
                goto Done;
        }

        //
        // convert Acl2 into Access Description Language and insert into htable for this blob
        //
        bAcl2NoExplicitAces = TRUE;
        if (dwAcl2AceCount < pAcl2->AceCount) {
            NtStatus = ScepConvertAclBlobToAdl(ObjectType,
                                               IsContainer,
                                               pAcl2,
                                               &dwAcl2AceCount,
                                               &bAcl2NoExplicitAces,
                                               hTable2);
            if ( !NT_SUCCESS(NtStatus) )
                goto Done;
        }

        //
        // compare Adls for Acl1 and Acl2 blobs
        // if after ignoring INHERITED_ACES, one Acl has no aces and the other has, then bAcl1NoExplicitAces != bAcl2NoExplicitAces
        //

        if (bAcl1NoExplicitAces != bAcl2NoExplicitAces || !ScepEqualAdls(hTable1, hTable2) ) {

            *pDifferent = TRUE;
            ScepFreeAdl(hTable1);
            ScepFreeAdl(hTable2);
            return(ERROR_SUCCESS);
        }

        //
        // need to reuse hTables for next blobs
        //

        ScepFreeAdl(hTable1);
        ScepFreeAdl(hTable2);

        //
        // the Adls are equal - so continue with next blobs for Acl1 and Acl2
        //
    }


Done:

    //
    // free in case goto was taken
    //

    ScepFreeAdl(hTable1);
    ScepFreeAdl(hTable2);

    return(RtlNtStatusToDosError(NtStatus));
}


NTSTATUS
ScepConvertAclBlobToAdl(
    IN      SE_OBJECT_TYPE  ObjectType,
    IN      BOOL    IsContainer,
    IN      PACL    pAcl,
    OUT     DWORD   *pdwAceNumber,
    OUT     BOOL    *pbAclNoExplicitAces,
    OUT     PSCEP_ADL_NODE *hTable
    )
/*
Routine Description:

    This routine builds the Adl for a contiguous block of same type aces.
    Inherited aces (INHERITED_ACE is set) are ignored.

Arguments:

    IN  ObjectType          - the type of object, passed on to other functions
    IN  IsContainer         - whether container or not, passed on to other functions
    IN  pAcl                - the Acl to be converted to Adl
    OUT pdwAceNumber        - running count of the aces considered
    OUT pbAclNoExplicitAces - whether there were explicit aces (if FALSE, there is at leat one explicit ace)
    OUT hTable              - the Adl structure for this Acl

Return Value:

    Win32 error codes
*/
{
    NTSTATUS        NtStatus=STATUS_SUCCESS;
    ACE_HEADER      *pAce=NULL;

    if (pAcl == NULL || pdwAceNumber == NULL ||
        hTable == NULL || pbAclNoExplicitAces == NULL) {

        return (STATUS_INVALID_PARAMETER);

    }

    DWORD dwAceNumber = *pdwAceNumber;

    NtStatus = RtlGetAce(pAcl, dwAceNumber, (PVOID *)&pAce);
    if ( !NT_SUCCESS(NtStatus) )
        goto Done;

    //
    // get the first non INHERITED_ACE
    //

    while ( (pAce->AceFlags & INHERITED_ACE)  &&  (++dwAceNumber < pAcl->AceCount) ) {

        NtStatus = RtlGetAce(pAcl, dwAceNumber, (PVOID *)&pAce);
        if ( !NT_SUCCESS(NtStatus) )
            goto Done;

    }

    if ( !(pAce->AceFlags & INHERITED_ACE) ) {

        UCHAR   AclAceType;

        *pbAclNoExplicitAces = FALSE;

        AclAceType = pAce->AceType;

        //
        // in a blob of AclAceType
        //
        while ( (pAce->AceType == AclAceType) &&  (dwAceNumber < pAcl->AceCount) ) {

            if (NO_ERROR != ScepAdlLookupAdd(ObjectType, IsContainer, pAce, hTable)){

                NtStatus = STATUS_NO_MEMORY;
                goto Done;

            }

            //
            // get the next ace in Acl
            //
            if (++dwAceNumber < pAcl->AceCount) {

                //
                // skip INHERITED_ACEs if any except if AceType changes
                //
                do {

                    NtStatus = RtlGetAce(pAcl, dwAceNumber, (PVOID *)&pAce);
                    if ( !NT_SUCCESS(NtStatus) )
                        goto Done;

                    //
                    // if AceType changes (e.g. from A to D) we quit building the Adl
                    // irrespective of whether it is an INHERITED_ACE
                    //

                    if (pAce->AceType != AclAceType)
                        break;

                } while ( (pAce->AceFlags & INHERITED_ACE) && (++dwAceNumber < pAcl->AceCount) );
            }
        }
    }

Done:
    //
    // update the running count of aces for this Acl
    //

    *pdwAceNumber = dwAceNumber;

    return(NtStatus);

}

BOOL
ScepEqualAdls(
    IN  PSCEP_ADL_NODE *hTable1,
    IN  PSCEP_ADL_NODE *hTable2
    )
/*
Routine Description:

    This routine compares rwo Adls - if the Adls are  equal, the hTables will be laid out in the same
    fashion since hashing function is the same. Two Adls are equal iff they match all of the below

        (a) SID, GIUD1, GIUD2
        (b) AceType
        (c) All the masks

Arguments:

    IN  hTable1     - the first Adl hash table
    IN  hTable2     - the secomd Adl hash table

Return Value:

    BOOL - true if equal
*/
{
    PSCEP_ADL_NODE    pNode1 = NULL;
    PSCEP_ADL_NODE    pNode2 = NULL;

    //
    // the Adls should be the same if superimposed over each other since they use the same hash etc.
    //

    for (DWORD   numBucket = 0; numBucket < SCEP_ADL_HTABLE_SIZE; numBucket++) {

        //
        // walk each bucket, marching pointers in pairs
        //

        pNode1 = hTable1[numBucket];
        pNode2 = hTable2[numBucket];

        while (pNode1 && pNode2) {

            if ( pNode1->AceType != pNode2->AceType ||
                 pNode1->dwEffectiveMask != pNode2->dwEffectiveMask ||
                 pNode1->dw_CI_IO_Mask != pNode2->dw_CI_IO_Mask ||
                 pNode1->dw_OI_IO_Mask != pNode2->dw_OI_IO_Mask ||
                 pNode1->dw_NP_CI_IO_Mask != pNode2->dw_NP_CI_IO_Mask ||
                 !ScepEqualSid(pNode1->pSid, pNode2->pSid) ||
                 !ScepEqualGuid(pNode1->pGuidObjectType, pNode2->pGuidObjectType) ||
                 !ScepEqualGuid(pNode1->pGuidInheritedObjectType, pNode2->pGuidInheritedObjectType) ) {

                return FALSE;
            }

            pNode1 = pNode1->Next;
            pNode2 = pNode2->Next;
        }

        if (pNode1 == NULL && pNode2 != NULL ||
            pNode1 != NULL && pNode2 == NULL) {
            return FALSE;
        }

    }

    return(TRUE);

}

DWORD
ScepAdlLookupAdd(
    IN      SE_OBJECT_TYPE ObjectType,
    IN      BOOL IsContainer,
    IN      ACE_HEADER   *pAce,
    OUT     PSCEP_ADL_NODE *hTable
    )
/*
Routine Description:

    This routine adds and initializes a new entry in the hTable for pAce->Sid or merges the
    existing access masks if pAce->Sid already exists

Arguments:

    IN  ObjectType          - the type of object, passed on to other functions
    IN  IsContainer         - whether container or not, passed on to other functions
    IN  pAce                - the ace to be parsed into the Adl hTable
    OUT hTable              - the hTable for this Adl

Return Value:

    Dos error codes
*/
{
    DWORD rc = NO_ERROR;
    PISID pSid = NULL;
    PSCEP_ADL_NODE  pNode = NULL;

    if (pAce == NULL || hTable == NULL)
        return ERROR_INVALID_PARAMETER;

    switch ( pAce->AceType ) {
    case ACCESS_ALLOWED_ACE_TYPE:
    case ACCESS_DENIED_ACE_TYPE:
    case SYSTEM_AUDIT_ACE_TYPE:
    case SYSTEM_ALARM_ACE_TYPE:

        pSid = (PISID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;

        break;

    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:

        pSid = (PISID)ScepObjectAceObjectType(pAce);

        break;

    default:
        // should not get in here taken care of just after switch
        ;
    }

    if (pSid == NULL)
        return(ERROR_INVALID_PARAMETER);

    pNode = ScepAdlLookup(pAce, hTable);

    //
    // hashed by last subauthority of  SID - will need to change this if too many collisions in hTable
    // once mapped to a bucket, for exact match, have to match the triple <SID,GUID1,GUID2>
    //

    if (pNode == NULL)

        //
        // seeing this triple <SID, GUID1, GUID2> for the first time
        //

        rc = ScepAddToAdlList( ObjectType,
                               IsContainer,
                               pAce,
                               &hTable[(pSid->SubAuthority[pSid->SubAuthorityCount - 1] % SCEP_ADL_HTABLE_SIZE)]
                               );


    else

        //
        // already exists so simply merge the masks
        //

        ScepAdlMergeMasks(ObjectType,
                          IsContainer,
                          pAce,
                          pNode
                          );

    return rc;

}


PSCEP_ADL_NODE
ScepAdlLookup(
    IN  ACE_HEADER   *pAce,
    IN  PSCEP_ADL_NODE *hTable
    )
/*
Routine Description:

    This routine searches searches the Adl hTable for the converted pAce's entry and returns
    a pointer to it if present, else returns NULL


Arguments:

    IN  pAce        - the Ace to convert to <SID,GUID1,GUID2> and search for
    IN  hTable      - the Adl in which pAce might exist

Return Value:

    The node corresponding to pAce if it found, else NULL
*/
{
    PSCEP_ADL_NODE  pNode;
    PISID   pSid = NULL;
    GUID    *pGuidObjectType = NULL;
    GUID    *pGuidInheritedObjectType = NULL;

    switch ( pAce->AceType ) {
    case ACCESS_ALLOWED_ACE_TYPE:
    case ACCESS_DENIED_ACE_TYPE:
    case SYSTEM_AUDIT_ACE_TYPE:
    case SYSTEM_ALARM_ACE_TYPE:

        pSid = (PISID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;

        break;

    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:

        pSid = (PISID)ScepObjectAceObjectType(pAce);
        pGuidObjectType = ScepObjectAceObjectType(pAce);
        pGuidInheritedObjectType = ScepObjectAceInheritedObjectType(pAce);

        break;

    default:
        // should not get in here since filtered out by caller ScepAdlLookupAdd()
        // in any case we do a check right after thsi switch
        ;

    }

    //
    // there might be something better we can do to handle this case
    //
    if (pSid == NULL)
        return NULL;


    for (pNode = hTable[(pSid->SubAuthority[pSid->SubAuthorityCount - 1] % SCEP_ADL_HTABLE_SIZE)];
         pNode != NULL; pNode = pNode->Next){

        if ( ScepEqualSid(pNode->pSid, pSid) &&
             ScepEqualGuid(pNode->pGuidObjectType, pGuidObjectType) &&
             ScepEqualGuid(pNode->pGuidInheritedObjectType, pGuidInheritedObjectType) ) {

                return pNode;
            }
    }
    return NULL;
}

DWORD
ScepAddToAdlList(
    IN      SE_OBJECT_TYPE ObjectType,
    IN      BOOL    IsContainer,
    IN      ACE_HEADER *pAce,
    OUT     PSCEP_ADL_NODE *pAdlList
    )
/*
Routine Description:

    This routine adds an ace to the head of the bucket into which pAce->Sid hashes (pAdlList)


Arguments:

    IN  ObjectType          - the type of object, passed on to other functions
    IN  IsContainer         - whether container or not, passed on to other functions
    IN  pAce                - the Ace to convert and add
    OUT pAdlList            - head of the bucket into which pAce->Sid hashes into

Return Value:

    Dos error code
*/
{

    PSCEP_ADL_NODE pNode=NULL;

    //
    // check arguments
    //
    if ( pAdlList == NULL || pAce == NULL )
        return(ERROR_INVALID_PARAMETER);

    //
    // allocate a new node
    //
    pNode = (PSCEP_ADL_NODE)ScepAlloc( (UINT)0, sizeof(SCEP_ADL_NODE));

    if ( pNode == NULL )
        return(ERROR_NOT_ENOUGH_MEMORY);

    pNode->pSid = NULL;
    pNode->pGuidObjectType = NULL;
    pNode->pGuidInheritedObjectType = NULL;
    pNode->AceType = pAce->AceType;
    pNode->Next = NULL;

    //
    // initialize the node with fields from pAce
    //

    switch ( pAce->AceType ) {
    case ACCESS_ALLOWED_ACE_TYPE:
    case ACCESS_DENIED_ACE_TYPE:
    case SYSTEM_AUDIT_ACE_TYPE:
    case SYSTEM_ALARM_ACE_TYPE:

        pNode->pSid = (PISID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;

        break;

    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:

        pNode->pSid = (PISID)ScepObjectAceObjectType(pAce);
        pNode->pGuidObjectType = ScepObjectAceObjectType(pAce);
        pNode->pGuidInheritedObjectType = ScepObjectAceInheritedObjectType(pAce);

        break;

    default:
        // should not get in here since filtered out by caller ScepAdlLookupAdd()
        ScepFree(pNode);
        return(ERROR_INVALID_PARAMETER);
        ;

    }

    //
    // initialize all masks for this node
    //

    pNode->dwEffectiveMask = 0;
    pNode->dw_CI_IO_Mask = 0;
    pNode->dw_OI_IO_Mask = 0;
    pNode->dw_NP_CI_IO_Mask = 0;

    ScepAdlMergeMasks(ObjectType,
                      IsContainer,
                      pAce,
                      pNode
                      );

    //
    // add the node to the front of the list and link its next to the old list
    //

    pNode->Next = *pAdlList;
    *pAdlList = pNode;

    return(NO_ERROR);
}

VOID
ScepAdlMergeMasks(
    IN  SE_OBJECT_TYPE  ObjectType,
    IN  BOOL    IsContainer,
    IN  ACE_HEADER  *pAce,
    IN  PSCEP_ADL_NODE pNode
    )
/*
Routine Description:

    The actual routine that merges the masks from pAce onto pNode


Arguments:

    IN  ObjectType          - the type of object, passed on to other functions
    IN  IsContainer         - whether container or not, passed on to other functions
    IN  pAce                - the Ace to extract flags and OR with (source)
    IN  pNode               - the Adl node to update masks (target)

Return Value:

    Nothing
*/
{
    DWORD   dwMask = 0;

    switch ( pAce->AceType ) {
    case ACCESS_ALLOWED_ACE_TYPE:
    case ACCESS_DENIED_ACE_TYPE:
    case SYSTEM_AUDIT_ACE_TYPE:
    case SYSTEM_ALARM_ACE_TYPE:
        dwMask = ((PACCESS_ALLOWED_ACE)pAce)->Mask;

        break;

    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:

        dwMask = ((PACCESS_ALLOWED_OBJECT_ACE)pAce)->Mask;

        break;

    default:
        // should not get in here since filtered out by all callers (3 deep)
        ;

    }

    //
    // if generic bits present, get the object specific masks
    //
    if ( dwMask & (GENERIC_READ |
                   GENERIC_WRITE |
                   GENERIC_EXECUTE |
                   GENERIC_ALL)) {

        switch ( ObjectType ) {
        case SE_DS_OBJECT:

            RtlMapGenericMask (
                              &dwMask,
                              &DsGenMap
                              );

            break;

        case SE_SERVICE:

            RtlMapGenericMask (
                              &dwMask,
                              &SvcGenMap
                              );
            break;

        case SE_REGISTRY_KEY:

            RtlMapGenericMask (
                              &dwMask,
                              &KeyGenericMapping
                              );
            break;

        case SE_FILE_OBJECT:

            RtlMapGenericMask (
                              &dwMask,
                              &FileGenericMapping
                              );
            break;

        default:
            // if this happens, dwMask is not mapped to object specific bits
            ;
        }
    }

    //
    // effective mask is updated for non-IO aces only
    //

    if ( !(pAce->AceFlags & INHERIT_ONLY_ACE) ) {
        pNode->dwEffectiveMask |= dwMask;
    }

    //
    // for non-containers, we don't care about the CI, OI masks (to simulate config)
    //

    if (IsContainer) {

        //
        // if NP, we only care about CI
        // else we care about CI, OI
        //

        if (pAce->AceFlags & NO_PROPAGATE_INHERIT_ACE) {

            if (pAce->AceFlags & CONTAINER_INHERIT_ACE) {
                pNode->dw_NP_CI_IO_Mask |= dwMask;
            }

        } else {

            if ( (pAce->AceFlags & CONTAINER_INHERIT_ACE) )
                pNode->dw_CI_IO_Mask |= dwMask;
            if ( !(ObjectType & SE_REGISTRY_KEY) && (pAce->AceFlags & OBJECT_INHERIT_ACE) )
                pNode->dw_OI_IO_Mask |= dwMask;

        }
    }

    return;

}

VOID
ScepFreeAdl(
    IN    PSCEP_ADL_NODE *hTable
    )
/*
Routine Description:

    This routine frees the linked lists of nodes (buckets) and reset's them for further use

Arguments:

    IN  hTable      - the hash-table to free

Return Value:

    Nothing
*/
{

    if (hTable) {
        for (UINT bucketNum = 0; bucketNum < SCEP_ADL_HTABLE_SIZE; bucketNum++ ) {
            ScepFreeAdlList(hTable[bucketNum]);
            hTable[bucketNum] = NULL;
        }
    }

}

SCESTATUS
ScepFreeAdlList(
   IN PSCEP_ADL_NODE pAdlList
   )
/*
Routine Description:

    This is the actual routine that frees the linked lists of nodes (buckets)

Arguments:

   IN   pAdlList    - head of bucket to free

Return Value:

    Nothing
*/
{
    PSCEP_ADL_NODE pCurAdlNode;
    PSCEP_ADL_NODE pTempNode;
    SCESTATUS      rc=SCESTATUS_SUCCESS;

    if ( pAdlList == NULL )
        return(rc);

    pCurAdlNode = pAdlList;
    while ( pCurAdlNode != NULL ) {

        pTempNode = pCurAdlNode;
        pCurAdlNode = pCurAdlNode->Next;

        __try {
            ScepFree( pTempNode );
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            rc = SCESTATUS_INVALID_PARAMETER;
        }

    }
    return(rc);
}


NTSTATUS
ScepAnyExplicitAcl(
    IN PACL Acl,
    IN DWORD Processed,
    OUT PBOOL pExist
    )
/*
Routine Description:

    This routine detects if there is any explicit ace in the Acl. The DWORD
    Processed is a bit mask of the aces already checked.

Arguments:

    Acl - The Acl

    Processed - The bit mask for the processed aces (so it won't be checked again)

    pExist - The output flag to indicate if there is any explicit ace

Return Value:

    NTSTATUS
*/
{
    NTSTATUS    NtStatus=STATUS_SUCCESS;
    DWORD       j;
    ACE_HEADER  *pAce=NULL;

    //
    // check output argument
    //
    if ( pExist == NULL )
        return(STATUS_INVALID_PARAMETER);

    *pExist = FALSE;

    if ( Acl == NULL )
        return(NtStatus);

    for ( j=0; j<Acl->AceCount; j++ ) {
        if ( Processed & (1 << j) )
            continue;

        NtStatus = RtlGetAce(Acl, j, (PVOID *)&pAce);
        if ( !NT_SUCCESS(NtStatus) )
            return(NtStatus);

        if ( pAce == NULL )
            continue;

        if ( !(pAce->AceFlags & INHERITED_ACE) ) {
            //
            // find a explicit Ace in Acl
            //
            *pExist = TRUE;
            break;
        }

    }

    return(NtStatus);

}


BOOL
ScepEqualAce(
    IN SE_OBJECT_TYPE ObjectType,
    IN BOOL IsContainer,
    IN ACE_HEADER *pAce1,
    IN ACE_HEADER *pAce2
    )
// compare two aces for exact match. The return BOOL value indicates the
// match or not
{
    PSID    pSid1=NULL, pSid2=NULL;
    ACCESS_MASK Access1=0, Access2=0;

    if ( pAce1 == NULL && pAce2 == NULL )
        return(TRUE);

    if ( pAce1 == NULL || pAce2 == NULL )
        return(FALSE);

    //
    // compare ace access type
    //
    if ( pAce1->AceType != pAce2->AceType )
        return(FALSE);


    if ( IsContainer ) {
        //
        // compare ace inheritance flag
        //
        if ( pAce1->AceFlags != pAce2->AceFlags )
            return(FALSE);
    }

    switch ( pAce1->AceType ) {
    case ACCESS_ALLOWED_ACE_TYPE:
    case ACCESS_DENIED_ACE_TYPE:
    case SYSTEM_AUDIT_ACE_TYPE:
    case SYSTEM_ALARM_ACE_TYPE:
        pSid1 = (PSID)&((PACCESS_ALLOWED_ACE)pAce1)->SidStart;
        pSid2 = (PSID)&((PACCESS_ALLOWED_ACE)pAce2)->SidStart;
        Access1 = ((PACCESS_ALLOWED_ACE)pAce1)->Mask;
        Access2 = ((PACCESS_ALLOWED_ACE)pAce2)->Mask;
        break;

    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:

        if ( ((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->Flags !=
             ((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->Flags ) {
            return(FALSE);
        }

        if ( ( ((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->Flags & ACE_OBJECT_TYPE_PRESENT ) ||
             ( ((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT ) ) {
            //
            // at least one GUID exists
            //
            if ( !ScepEqualGuid( (GUID *)&((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->ObjectType,
                               (GUID *)&((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->ObjectType ) ) {
                return(FALSE);
            }

            if ( ( ((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->Flags & ACE_OBJECT_TYPE_PRESENT ) &&
                 ( ((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT ) ) {
                //
                // the second GUID also exists
                //
                if ( !ScepEqualGuid( (GUID *)&((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->InheritedObjectType,
                                   (GUID *)&((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->InheritedObjectType) ) {
                    return(FALSE);
                }

                pSid1 = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->SidStart;
                pSid2 = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->SidStart;

            } else {

                pSid1 = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->InheritedObjectType;
                pSid2 = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->InheritedObjectType;
            }

        } else {

            //
            // none of the GUID exists
            //
            pSid1 = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->ObjectType;
            pSid2 = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->ObjectType;
        }

        Access1 = ((PACCESS_ALLOWED_OBJECT_ACE)pAce1)->Mask;
        Access2 = ((PACCESS_ALLOWED_OBJECT_ACE)pAce2)->Mask;


        break;
    default:
        return(FALSE); // not recognized Ace type
    }

    if ( pSid1 == NULL || pSid2 == NULL )
        //
        // no Sid, ignore the Ace
        //
        return(FALSE);

    //
    // compare the sids
    //
    if ( !EqualSid(pSid1, pSid2) )
        return(FALSE);

    //
    // access mask
    //
    // Translation is already done when calculating security descriptor
    // for file objects and registry objects
    //
    if ( Access1 != Access2 ) {
        switch ( ObjectType ) {
        case SE_DS_OBJECT:
            //
            // convert access mask of Access2 (from ProfileSD) for ds objects
            //

            RtlMapGenericMask (
                &Access2,
                &DsGenMap
                );
            if ( Access1 != Access2)
                return(FALSE);
            break;

        case SE_SERVICE:

            RtlMapGenericMask (
                &Access2,
                &SvcGenMap
                );
            if ( Access1 != Access2)
                return(FALSE);
            break;

        case SE_REGISTRY_KEY:

            RtlMapGenericMask (
                &Access2,
                &KeyGenericMapping
                );
            if ( Access1 != Access2)
                return(FALSE);
            break;

        case SE_FILE_OBJECT:

            RtlMapGenericMask (
                &Access2,
                &FileGenericMapping
                );
            if ( Access1 != Access2)
                return(FALSE);
            break;

        default:
            return(FALSE);
        }
    }

    return(TRUE);
}



SCESTATUS
ScepAddToNameStatusList(
    OUT PSCE_NAME_STATUS_LIST *pNameList,
    IN PWSTR Name,
    IN ULONG Len,
    IN DWORD Status
    )
/* ++
Routine Description:

    This routine adds a name (wchar) and a status to the name list.


Arguments:

    pNameList -  The name list to add to.

    Name      -  The name to add

    Len       -  number of wchars to add

    Status    -  The value for the status field

Return value:

    Win32 error code
-- */
{

    PSCE_NAME_STATUS_LIST pList=NULL;
    ULONG  Length=Len;

    if ( pNameList == NULL )
        return(ERROR_INVALID_PARAMETER);

    if ( Name != NULL && Name[0] && Len == 0 )
        Length = wcslen(Name) + 1;

//    if ( Length <= 1)
//        return(NO_ERROR);

    pList = (PSCE_NAME_STATUS_LIST)ScepAlloc( (UINT)0, sizeof(SCE_NAME_STATUS_LIST));

    if ( pList == NULL )
        return(ERROR_NOT_ENOUGH_MEMORY);

    if ( Name != NULL && Name[0] ) {
        pList->Name = (PWSTR)ScepAlloc( LMEM_ZEROINIT, (Length+1)*sizeof(TCHAR));
        if ( pList->Name == NULL ) {
            ScepFree(pList);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        wcsncpy(pList->Name, Name, Length);
    } else
        pList->Name = NULL;

    pList->Status = Status;
    pList->Next = *pNameList;
    *pNameList = pList;

    return(NO_ERROR);
}



DWORD
ScepAddToObjectList(
    OUT PSCE_OBJECT_LIST  *pNameList,
    IN PWSTR  Name,
    IN ULONG  Len,
    IN BOOL  IsContainer,
    IN BYTE  Status,
    IN DWORD  Count,
    IN BYTE byFlags
    )
/* ++
Routine Description:

    This routine adds a name (wchar), a status, and a count to the name list.


Arguments:

    pNameList -  The name list to add to.

    Name      -  The name to add

    Len       -  number of wchars to add

    Status    -  The value for the status field

    Count     -  The value for the count field

    byFlags   -  SCE_CHECK_DUP do not add for duplicates
                 SCE_INCREASE_COUNT increase count by 1

Return value:

    Win32 error code
-- */
{

    PSCE_OBJECT_LIST pList=NULL;
    ULONG  Length=Len;

    if ( pNameList == NULL )
        return(ERROR_INVALID_PARAMETER);

    if ( Name == NULL )
        return(NO_ERROR);

    if ( Len == 0 )
         Length = wcslen(Name);

     if ( Length < 1)
         return(NO_ERROR);

    if ( byFlags & SCE_CHECK_DUP ) {
        for ( pList = *pNameList; pList != NULL; pList = pList->Next ) {
            if ( _wcsnicmp( pList->Name, Name, Length) == 0 &&
                 pList->Name[Length] == L'\0') {
                break;
            }
        }
        if ( NULL != pList ) {
            //
            // already exist. return
            //
            if ( (byFlags & SCE_INCREASE_COUNT) && 0 == pList->Count ) {
                pList->Count++;
            }
            return(NO_ERROR);
        }
    }

    pList = (PSCE_OBJECT_LIST)ScepAlloc( (UINT)0, sizeof(SCE_OBJECT_LIST));

    if ( pList == NULL )
        return(ERROR_NOT_ENOUGH_MEMORY);

    pList->Name = (PWSTR)ScepAlloc( LMEM_ZEROINIT, (Length+1)*sizeof(TCHAR));
    if ( pList->Name == NULL ) {
        ScepFree(pList);
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    wcsncpy(pList->Name, Name, Length);
    pList->Status = Status;
    pList->IsContainer = IsContainer;

    if ( byFlags & SCE_INCREASE_COUNT && 0 == Count )
        pList->Count = 1;
    else
        pList->Count = Count;

    pList->Next = *pNameList;
    *pNameList = pList;

    return(NO_ERROR);
}



DWORD
ScepGetNTDirectory(
    IN PWSTR *ppDirectory,
    IN PDWORD pDirSize,
    IN DWORD  Flag
    )
/*
Routine Description:

    This routine retrieves windows directory location or system directory
    location based on the input Flag. The output directory location must
    be freed by LocalFree after use.

Arguments:

    ppDirectory - the output buffer holding the directory location.

    pDirSize - The returned number of wchars of the output buffer

    Flag  - Flag to indicate directory
                1 = Windows directory
                2 = System directory

Return Value:

    Win32 error codes
*/
{
    DWORD  dSize=0;
    DWORD  rc=0;
    PWSTR pSubKey=NULL;
    PWSTR pValName=NULL;

    if ( ppDirectory == NULL )
        return(ERROR_INVALID_PARAMETER);

    switch ( Flag ) {
    case SCE_FLAG_WINDOWS_DIR:  // windows directory
        dSize=GetSystemWindowsDirectory( *ppDirectory, 0 );
        break;
    case SCE_FLAG_SYSTEM_DIR: // system directory
        dSize=GetSystemDirectory( *ppDirectory, 0 );
        break;

    case SCE_FLAG_DSDIT_DIR: // DS working directory
    case SCE_FLAG_DSLOG_DIR: // DS database log files directory
    case SCE_FLAG_SYSVOL_DIR: // Sysvol directory
    case SCE_FLAG_BOOT_DRIVE: // boot drive

        // get the appropriate registry path and value name
        if ( SCE_FLAG_SYSVOL_DIR == Flag ) {
            pSubKey = szNetlogonKey;
            pValName = szSysvolValue;

        } else if ( SCE_FLAG_BOOT_DRIVE == Flag ) {
            pSubKey = szSetupKey;
            pValName = szBootDriveValue;

        } else {
            pSubKey = szNTDSKey;
            if ( SCE_FLAG_DSDIT_DIR == Flag ) {
                pValName = szDSDITValue;
            } else {
                pValName = szDSLOGValue;
            }
        }

        //
        // query the value.
        // if this function is executed on a non DC, this function will fail
        // possibly with ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND
        // which in turn fails the translation.
        //
        DWORD RegType;
        rc = ScepRegQueryValue(
                HKEY_LOCAL_MACHINE,
                pSubKey,
                pValName,
                (PVOID *)ppDirectory,
                &RegType
                );

        if ( rc == ERROR_SUCCESS && RegType != REG_SZ ) {
            rc = ERROR_FILE_NOT_FOUND;
        }

        if ( rc == ERROR_SUCCESS && *ppDirectory ) {

            if ( Flag == SCE_FLAG_SYSVOL_DIR ) {
                //
                // for sysvol path, it will look like d:\winnt\sysvol\sysvol.
                // we need to remove the last sysvol from this variable
                //
                PWSTR pTemp = ScepWcstrr(*ppDirectory, L"\\sysvol");
                if ( pTemp && (pTemp != *ppDirectory) &&
                     _wcsnicmp(pTemp-7, L"\\sysvol",7 ) == 0 ) {

                    // terminate the string here
                    *pTemp = L'\0';
                }
            }

            dSize = wcslen(*ppDirectory);
        }

        break;

    default:  // invalid
        return(ERROR_INVALID_PARAMETER);
        break;
    }

    if ( dSize > 0 &&
         ( SCE_FLAG_WINDOWS_DIR == Flag ||
           SCE_FLAG_SYSTEM_DIR == Flag ) ) {

        *ppDirectory = (PWSTR)ScepAlloc(LMEM_ZEROINIT, (dSize+1)*sizeof(WCHAR));
        if ( *ppDirectory == NULL )
            return(ERROR_NOT_ENOUGH_MEMORY);

        switch ( Flag ) {
        case SCE_FLAG_WINDOWS_DIR:  // windows directory
            dSize=GetSystemWindowsDirectory( *ppDirectory, dSize );
            break;
        case SCE_FLAG_SYSTEM_DIR: // system directory
            dSize=GetSystemDirectory( *ppDirectory, dSize );
            break;
        }

    }
    *pDirSize = dSize;

    if ( dSize == 0 ) {
        if ( *ppDirectory != NULL )
            ScepFree(*ppDirectory);
        *ppDirectory = NULL;

        if ( rc ) {
            return(rc);
        } else if ( NO_ERROR == GetLastError() )
            return(ERROR_INVALID_DATA);
        else
            return(GetLastError());
    } else
        _wcsupr(*ppDirectory);

    return(NO_ERROR);

}



DWORD
ScepGetCurrentUserProfilePath(
    OUT PWSTR *ProfilePath
    )
{

    HANDLE          Token;
    NTSTATUS        NtStatus;
    DWORD           rc;
    PVOID           Info=NULL;
    DWORD           ReturnLen, NewLen;
    UNICODE_STRING  ProfileName;


    if (!OpenThreadToken( GetCurrentThread(),
                          TOKEN_QUERY,
                          FALSE,
                          &Token)) {

        if (!OpenProcessToken( GetCurrentProcess(),
                               TOKEN_QUERY,
                               &Token))

            return(GetLastError());

    }

    //
    // get token user
    //
    NtStatus = NtQueryInformationToken (
                    Token,
                    TokenUser,
                    NULL,
                    0,
                    &ReturnLen
                    );
    if ( NtStatus == STATUS_BUFFER_TOO_SMALL ) {
        //
        // allocate buffer
        //
        Info = ScepAlloc(0, ReturnLen+1);

        if ( Info != NULL ) {
            NtStatus = NtQueryInformationToken (
                            Token,
                            TokenUser,
                            Info,
                            ReturnLen,
                            &NewLen
                            );
            if ( NT_SUCCESS(NtStatus) ) {

                ProfileName.Length = 0;

                rc = ScepGetUsersProfileName(
                        ProfileName,
                        ((PTOKEN_USER)Info)->User.Sid,
                        FALSE,
                        ProfilePath
                        );
            } else
                rc = RtlNtStatusToDosError(NtStatus);

            ScepFree(Info);

        } else {
            rc = ERROR_NOT_ENOUGH_MEMORY;
        }

    } else
        rc = RtlNtStatusToDosError(NtStatus);

    CloseHandle(Token);

    return(rc);

}



DWORD
ScepGetUsersProfileName(
    IN UNICODE_STRING AssignedProfile,
    IN PSID AccountSid,
    IN BOOL bDefault,
    OUT PWSTR *UserProfilePath
    )
{
    DWORD                       rc=ERROR_INVALID_PARAMETER;
    SID_IDENTIFIER_AUTHORITY    *a;
    DWORD                       Len, i, j;
    WCHAR                       KeyName[356];
    PWSTR                       StrValue=NULL;
    PWSTR                       SystemRoot=NULL;
    DWORD                       DirSize=0;


    if ( AssignedProfile.Length > 0 && AssignedProfile.Buffer != NULL ) {
        //
        // use the assigned profile
        //
        *UserProfilePath = (PWSTR)ScepAlloc( LMEM_ZEROINIT, AssignedProfile.Length+2);
        if ( *UserProfilePath == NULL )
            return(ERROR_NOT_ENOUGH_MEMORY);

        wcsncpy(*UserProfilePath, AssignedProfile.Buffer, AssignedProfile.Length/2);
        return(NO_ERROR);

    }

    if ( AccountSid != NULL ) {
        //
        // look for this user's ProfileImageName in ProfileList in registry
        // if this user logged on the system once
        //

        memset(KeyName, '\0', 356*sizeof(WCHAR));

        swprintf(KeyName, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
        Len = wcslen(KeyName);

        a = RtlIdentifierAuthoritySid(AccountSid);

        swprintf(KeyName+Len, L"S-1-");
        for ( i=0; i<6; i++ ) {
            if ( a -> Value[i] > 0 )
                break;
        }
        for ( j=i; j<6; j++) {
            swprintf(KeyName+Len, L"%s%d", KeyName+Len, a -> Value[j]);
        }

        for (i = 0; i < *RtlSubAuthorityCountSid(AccountSid); i++) {
            swprintf(KeyName+Len, L"%s-%d", KeyName+Len, *RtlSubAuthoritySid(AccountSid, i));
        }
        //
        // now the registry full path name for the user profile is built into KeyName
        //
        rc = ScepRegQueryValue(
                 HKEY_LOCAL_MACHINE,
                 KeyName,
                 L"ProfileImagePath",
                 (PVOID *)&StrValue,
                 &Len
                 );

        if ( rc == NO_ERROR && StrValue != NULL ) {
            //
            // translatethe name to expand environment variables
            //
            DirSize = ExpandEnvironmentStrings(StrValue, NULL, 0);
            if ( DirSize ) {

                *UserProfilePath = (PWSTR)ScepAlloc(0, (DirSize+1)*sizeof(WCHAR));
                if ( *UserProfilePath ) {

                    if ( !ExpandEnvironmentStrings(StrValue, *UserProfilePath, DirSize) ) {
                        // error occurs
                        rc = GetLastError();

                        ScepFree(*UserProfilePath);
                        *UserProfilePath = NULL;
                    }

                } else {
                    rc = ERROR_NOT_ENOUGH_MEMORY;
                }
            } else {
                rc = GetLastError();
            }

            ScepFree(StrValue);

            return(rc);

        }
    }
    if ( StrValue ) {
        ScepFree(StrValue);
        StrValue = NULL;
    }
    //
    // if user is not assigned a profile explicitly, and there is no
    // profile created (under ProfileList), take the default profile
    //
    if ( bDefault ) {

        rc = NO_ERROR;

#if _WINNT_WIN32>=0x0500
        //
        // Take the default user profile
        //
        DirSize = 355;
        GetDefaultUserProfileDirectory(KeyName, &DirSize);

        if ( DirSize ) {
            //
            // length of "\\NTUSER.DAT" is 11
            //
            *UserProfilePath = (PWSTR)ScepAlloc( 0, (DirSize+12)*sizeof(WCHAR));

            if ( *UserProfilePath ) {
                if ( DirSize > 355 ) {
                    //
                    // KeyName buffer is not enough, call again
                    //
                    Len = DirSize;
                    if ( !GetDefaultUserProfileDirectory(*UserProfilePath, &Len) ) {
                        //
                        // error occurs, free the buffer
                        //
                        rc = GetLastError();

                        ScepFree(*UserProfilePath);
                        *UserProfilePath = NULL;
                    }

                } else {
                    //
                    // KeyName contains the directory
                    //
                    wcscpy(*UserProfilePath, KeyName);
                    (*UserProfilePath)[DirSize] = L'\0';
                }
                //
                // append NTUSER.DAT to the end
                //
                if ( NO_ERROR == rc ) {
                    wcscat(*UserProfilePath, L"\\NTUSER.DAT");
                }

            } else {
                rc = ERROR_NOT_ENOUGH_MEMORY;
            }
        } else {
            rc = GetLastError();
        }

#else
        //
        // for NT4: Take the default user profile
        //
        rc = ScepGetNTDirectory( &SystemRoot, &DirSize, SCE_FLAG_WINDOWS_DIR );

        if ( NO_ERROR == rc ) {
            //
            // string to append to the %SystemRoot%
            //
            wcscpy(KeyName, L"\\Profiles\\Default User\\NTUSER.DAT");
            Len = wcslen(KeyName);

            *UserProfilePath = (PWSTR)ScepAlloc( 0, (DirSize+Len+1)*sizeof(WCHAR));

            if ( *UserProfilePath == NULL ) {
                rc = ERROR_NOT_ENOUGH_MEMORY;
            } else {
                swprintf( *UserProfilePath, L"%s%s", SystemRoot, KeyName);
            }
        }
        if ( SystemRoot != NULL )
            ScepFree( SystemRoot);

#endif
    }

    return(rc);

}


DWORD
SceAdjustPrivilege(
    IN  ULONG           Priv,
    IN  BOOL            Enable,
    IN  HANDLE          TokenToAdjust
    )
/* ++

Routine Description:

   This routine enable/disable the specified privilege (Priv) to the current process.

Arguments:

   Priv  - The privilege to adjust

   Enable - TRUE = enable, FALSE = disable

   TokenToAdjust - The Token of current thread/process. It is optional

Return value:

   Win32 error code

-- */
{
    HANDLE          Token;
    NTSTATUS        Status;
    TOKEN_PRIVILEGES    Privs;

    if ( TokenToAdjust == NULL ) {
        if (!OpenThreadToken( GetCurrentThread(),
                              TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                              FALSE,
                              &Token)) {

            if (!OpenProcessToken( GetCurrentProcess(),
                                   TOKEN_ADJUST_PRIVILEGES,
                                   &Token))

                return(GetLastError());

        }
    } else
        Token = TokenToAdjust;

    //
    // Token_privileges contains enough room for one privilege.
    //

    Privs.PrivilegeCount = 1;
    Privs.Privileges[0].Luid = RtlConvertUlongToLuid(Priv); // RtlConvertLongToLuid(Priv);
    Privs.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;

    Status = NtAdjustPrivilegesToken(Token,
                                     FALSE,
                                     &Privs,
                                     0,
                                     NULL,
                                     0);

    if (TokenToAdjust == NULL )
        CloseHandle(Token);

    return (RtlNtStatusToDosError( Status ) );
}


DWORD
ScepGetEnvStringSize(
    IN LPVOID peb
    )
{

    if ( !peb ) {
        return 0;
    }

    DWORD dwSize=0;

    LPTSTR pTemp=(LPTSTR)peb;
    DWORD Len;

    while ( *pTemp ) {
        Len = wcslen(pTemp);
        dwSize += Len+1;

        pTemp += Len+1;

    };

    dwSize++;

    return dwSize*sizeof(WCHAR);
}


//*************************************************************
// Routines to handle events
//*************************************************************

BOOL InitializeEvents (
    IN LPTSTR EventSourceName
    )
/*++

Routine Description:

    Opens the event log

Arguments:

    EventSourceName - the event's source name (usually dll or exe's name)

Return:

    TRUE if successful
    FALSE if an error occurs

--*/
{

    if ( hEventLog ) {
        //
        // already initialized
        //
        return TRUE;
    }

    //
    // Open the event source
    //

    if ( EventSourceName ) {

        wcscpy(EventSource, EventSourceName);

        hEventLog = RegisterEventSource(NULL, EventSource);

        if (hEventLog) {
            return TRUE;
        }

    } else {
        EventSource[0] = L'\0';
    }

    return FALSE;
}

int
LogEvent(
    IN HINSTANCE hInstance,
    IN DWORD LogLevel,
    IN DWORD dwEventID,
    IN UINT  idMsg,
    ...)
/*++

Routine Description:

    Logs a verbose event to the event log

Arguments:

    hInstance   - the resource dll instance

    bLogLevel   - the severity level of the log
                        STATUS_SEVERITY_INFORMATIONAL
                        STATUS_SEVERITY_WARNING
                        STATUS_SEVERITY_ERROR

    dwEventID   - the event ID (defined in uevents.mc)

    idMsg       - Message id

Return:

    TRUE if successful
    FALSE if an error occurs

--*/
{
    TCHAR szMsg[MAX_PATH];
    TCHAR szErrorMsg[2*MAX_PATH+40];
    LPTSTR aStrings[2];
    WORD wType;
    va_list marker;


    //
    // Check for the event log being open.
    //

    if (!hEventLog ) {

        if ( EventSource[0] == L'\0' ||
             !InitializeEvents(EventSource)) {
            return -1;
        }
    }


    //
    // Load the message
    //

    if (idMsg != 0) {
        if (!LoadString (hInstance, idMsg, szMsg, MAX_PATH)) {
            return -1;
        }

    } else {
        lstrcpy (szMsg, TEXT("%s"));
    }


    //
    // Plug in the arguments
    //
    szErrorMsg[0] = L'\0';
    va_start(marker, idMsg);

    __try {
        wvsprintf(szErrorMsg, szMsg, marker);
    } __except(EXCEPTION_EXECUTE_HANDLER) {

    }
    va_end(marker);


    //
    // Report the event to the eventlog
    //

    aStrings[0] = szErrorMsg;

    switch (LogLevel) {
    case STATUS_SEVERITY_WARNING:
        wType = EVENTLOG_WARNING_TYPE;
        break;
    case STATUS_SEVERITY_ERROR:
        wType = EVENTLOG_ERROR_TYPE;
        break;
    default:
        wType = EVENTLOG_INFORMATION_TYPE;
        break;
    }

    if (!ReportEvent(hEventLog,
                     wType,
                     0,
                     dwEventID,
                     NULL,
                     1,
                     0,
                     (LPCTSTR *)aStrings,
                     NULL) ) {
        return 1;
    }

    return 0;
}


int
LogEventAndReport(
    IN HINSTANCE hInstance,
    IN LPTSTR LogFileName,
    IN DWORD LogLevel,
    IN DWORD dwEventID,
    IN UINT  idMsg,
    ...)
/*++

Routine Description:

    Logs a verbose event to the event log and logs

Arguments:

    hInstance   - the resource dll handle

    LofFileName - the log file also reported to

    bLogLevel   - the severity level of the log
                        STATUS_SEVERITY_INFORMATIONAL
                        STATUS_SEVERITY_WARNING
                        STATUS_SEVERITY_ERROR

    dwEventID   - the event ID (defined in uevents.mc)

    idMsg       - Message id

Return:

    TRUE if successful
    FALSE if an error occurs

--*/
{
    TCHAR szMsg[MAX_PATH];
    TCHAR szErrorMsg[2*MAX_PATH+40];
    LPTSTR aStrings[2];
    WORD wType;
    va_list marker;


    //
    // Load the message
    //

    if (idMsg != 0) {
        if (!LoadString (hInstance, idMsg, szMsg, MAX_PATH)) {
            return -1;
        }

    } else {
        lstrcpy (szMsg, TEXT("%s"));
    }

    HANDLE hFile = INVALID_HANDLE_VALUE;
    if ( LogFileName ) {
        hFile = CreateFile(LogFileName,
                           GENERIC_WRITE,
                           FILE_SHARE_READ,
                           NULL,
                           OPEN_ALWAYS,
                           FILE_ATTRIBUTE_NORMAL,
                           NULL);

        if (hFile != INVALID_HANDLE_VALUE) {

            DWORD dwBytesWritten;

            SetFilePointer (hFile, 0, NULL, FILE_BEGIN);

            BYTE TmpBuf[3];
            TmpBuf[0] = 0xFF;
            TmpBuf[1] = 0xFE;
            TmpBuf[2] = 0;

            WriteFile (hFile, (LPCVOID)TmpBuf, 2,
                       &dwBytesWritten,
                       NULL);

            SetFilePointer (hFile, 0, NULL, FILE_END);
        }
    }

    //
    // Check for the event log being open.
    //

    if (!hEventLog && dwEventID > 0 ) {

        if ( EventSource[0] == L'\0' ||
             !InitializeEvents(EventSource)) {

            if ( INVALID_HANDLE_VALUE == hFile ) {
                return -1;   // no event log and the log file can't be opened
            }
        }
    }

    //
    // Plug in the arguments
    //
    szErrorMsg[0] = L'\0';
    va_start(marker, idMsg);

    __try {
        wvsprintf(szErrorMsg, szMsg, marker);
    } __except(EXCEPTION_EXECUTE_HANDLER) {

    }
    va_end(marker);


    //
    // Report the event to the eventlog
    //

    int iRet = 0;

    if ( hEventLog && dwEventID > 0 ) {

        aStrings[0] = szErrorMsg;

        switch (LogLevel) {
        case STATUS_SEVERITY_WARNING:
            wType = EVENTLOG_WARNING_TYPE;
            break;
        case STATUS_SEVERITY_ERROR:
            wType = EVENTLOG_ERROR_TYPE;
            break;
        default:
            wType = EVENTLOG_INFORMATION_TYPE;
            break;
        }

        if (!ReportEvent(hEventLog,
                         wType,
                         0,
                         dwEventID,
                         NULL,
                         1,
                         0,
                         (LPCTSTR *)aStrings,
                         NULL) ) {
            iRet = 1;
        }
    }

    if ( INVALID_HANDLE_VALUE != hFile ) {
        //
        // Log to the log file
        //
        ScepWriteSingleUnicodeLog(hFile, FALSE, L"\r\n");
        ScepWriteSingleUnicodeLog(hFile, TRUE, szErrorMsg );

        CloseHandle(hFile);
    }

    return iRet;
}


BOOL
ShutdownEvents (void)
/*++
Routine Description:

    Stops the event log

Arguments:

    None

Return:

    TRUE if successful
    FALSE if an error occurs
--*/
{
    BOOL bRetVal = TRUE;
    HANDLE hTemp = hEventLog;

    hEventLog = NULL;
    if (hTemp) {
        bRetVal = DeregisterEventSource(hTemp);
    }

    EventSource[0] = L'\0';
    return bRetVal;
}


SCESTATUS
ScepConvertToSDDLFormat(
    IN LPTSTR pszValue,
    IN DWORD Len
    )
{
    if ( pszValue == NULL || Len == 0 ) {
        return SCESTATUS_INVALID_PARAMETER;
    }

    ScepConvertSDDLSid(pszValue, L"DA", L"BA");

    ScepConvertSDDLSid(pszValue, L"RP", L"RE");

    ScepConvertSDDLAceType(pszValue, L"SA", L"AU");

    ScepConvertSDDLAceType(pszValue, L"SM", L"AL");

    ScepConvertSDDLAceType(pszValue, L"OM", L"OL");

    return SCESTATUS_SUCCESS;

}


BOOL
ScepConvertSDDLSid(
    LPTSTR  pszValue,
    PCWSTR  szSearchFor,  // only two letters are allowed
    PCWSTR  szReplace
    )
{

    PWSTR pTemp = pszValue;
    DWORD i;

    while ( pTemp && *pTemp != L'\0' ) {

        pTemp = wcsstr(pTemp, szSearchFor);

        if ( pTemp != NULL ) {

            //
            // find the first non space char
            // must be : or ;
            //
            i=1;

            while ( pTemp-i > pszValue && *(pTemp-i) == L' ' ) {
                i++;
            }

            if ( pTemp-i > pszValue &&
                 ( *(pTemp-i) == L':' || *(pTemp-i) == L';') ) {

                //
                // find the next non space char
                // must be ), O:, G:, D:, S:
                //

                i=2;
                while ( *(pTemp+i) == L' ' ) {
                    i++;
                }

                if ( *(pTemp+i) == L')' ||
                     ( *(pTemp+i) != L'\0' && *(pTemp+i+1) == L':')) {
                    //
                    // find one, replace it
                    //
                    *pTemp = szReplace[0];
                    *(pTemp+1) = szReplace[1];
                }

                pTemp += 2;

            } else {

                //
                // this is not a one to convert
                //
                pTemp += 2;
            }
        }
    }

    return TRUE;
}


BOOL
ScepConvertSDDLAceType(
    LPTSTR  pszValue,
    PCWSTR  szSearchFor,  // only two letters are allowed
    PCWSTR  szReplace
    )
{

    PWSTR pTemp = pszValue;
    DWORD i;

    while ( pTemp && *pTemp != L'\0' ) {

        pTemp = wcsstr(pTemp, szSearchFor);

        if ( pTemp != NULL ) {

            //
            // find the first non space char
            // must be (
            //
            i=1;

            while ( pTemp-i > pszValue && *(pTemp-i) == L' ' ) {
                i++;
            }

            if ( pTemp-i > pszValue &&
                 ( *(pTemp-i) == L'(') ) {

                //
                // find the next non space char
                // must be ;
                //

                i=2;
                while ( *(pTemp+i) == L' ' ) {
                    i++;
                }

                if ( *(pTemp+i) == L';' ) {
                    //
                    // find one, replace it with AU
                    //
                    *pTemp = szReplace[0];
                    *(pTemp+1) = szReplace[1];
                }

                pTemp += 2;

            } else {

                //
                // this is not a one to convert
                //
                pTemp += 2;
            }
        }
    }

    return TRUE;
}

BOOL
SceIsSystemDatabase(
    IN LPCTSTR DatabaseName
    )
/*
Routine Description:

    Determine if the given database is the default system database

Argument:

    DatabaseName    - the database name (full path)

Return Value:

    TRUE    - the given database is the system database

    FALSE   - the database is not the system database or error occurred
              GetLastError() to get the error.
*/
{

    if ( DatabaseName == NULL ) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    DWORD rc;
    PWSTR DefProfile=NULL;
    DWORD RegType;

/*
    // do not save system database in registry
    // always "hardcoded" to %windir%\security\database
    // query the system database name
    //
    rc = ScepRegQueryValue(
            HKEY_LOCAL_MACHINE,
            SCE_ROOT_PATH,
            L"DefaultProfile",
            (PVOID *)&DefProfile,
            &RegType
            );

    if ( rc != NO_ERROR ) {
*/
        //
        // use the default
        //
        PWSTR SysRoot=NULL;

        RegType =  0;

        rc = ScepGetNTDirectory( &SysRoot, &RegType, SCE_FLAG_WINDOWS_DIR );

        if ( rc == NO_ERROR ) {

            if ( SysRoot != NULL ) {

                //
                // default location is %SystemRoot%\Security\Database\secedit.sdb
                //
                TCHAR TempName[256];

                wcscpy(TempName, L"\\Security\\Database\\secedit.sdb");
                RegType += wcslen(TempName)+1;

                DefProfile = (PWSTR)ScepAlloc( 0, RegType*sizeof(WCHAR));
                if ( DefProfile != NULL ) {
                    swprintf(DefProfile, L"%s%s", SysRoot, TempName );

                    *(DefProfile+RegType-1) = L'\0';

                } else
                    rc = ERROR_NOT_ENOUGH_MEMORY;


                ScepFree(SysRoot);

            } else
                rc = ERROR_INVALID_DATA;
        }
//    }

    BOOL bRet=FALSE;

    if ( (rc == NO_ERROR) && DefProfile ) {

        if ( _wcsicmp(DefProfile, DatabaseName) == 0 ) {
            //
            // this is the system database
            //
            bRet = TRUE;
        }
    }

    ScepFree(DefProfile);

    //
    // set last error and return
    //
    if ( bRet ) {
        SetLastError(ERROR_SUCCESS);
    } else {
        SetLastError(rc);
    }

    return(bRet);
}

DWORD
ScepWriteVariableUnicodeLog(
    IN HANDLE hFile,
    IN BOOL bAddCRLF,
    IN LPTSTR szFormat,
    ...
    )
{
    if ( INVALID_HANDLE_VALUE == hFile || NULL == hFile ||
         NULL == szFormat ) {
        return(ERROR_INVALID_PARAMETER);
    }

    va_list            args;
    LPTSTR             lpDebugBuffer;
    DWORD              rc=ERROR_NOT_ENOUGH_MEMORY;

    lpDebugBuffer = (LPTSTR) LocalAlloc (LPTR, 2048 * sizeof(TCHAR));

    if (lpDebugBuffer) {

        va_start( args, szFormat );

        _vsnwprintf(lpDebugBuffer, 2048 - 1, szFormat, args);

        va_end( args );

        //
        // always put a CR/LF at the end
        //

        DWORD dwBytesWritten;

        if ( WriteFile (hFile, (LPCVOID) lpDebugBuffer,
                       wcslen (lpDebugBuffer) * sizeof(WCHAR),
                       &dwBytesWritten,
                       NULL) ) {

            if ( bAddCRLF ) {

                WriteFile (hFile, (LPCVOID) c_szCRLF,
                           2 * sizeof(WCHAR),
                           &dwBytesWritten,
                           NULL);
            }

            rc = ERROR_SUCCESS;

        } else {
            rc = GetLastError();
        }

        LocalFree(lpDebugBuffer);

    }

    return(rc);

}


DWORD
ScepWriteSingleUnicodeLog(
    IN HANDLE hFile,
    IN BOOL bAddCRLF,
    IN LPWSTR szMsg
    )
{
    if ( INVALID_HANDLE_VALUE == hFile ) {
        return(ERROR_INVALID_PARAMETER);
    }

    DWORD dwBytesWritten;

    if ( WriteFile (hFile, (LPCVOID) szMsg,
                   wcslen (szMsg) * sizeof(WCHAR),
                   &dwBytesWritten,
                   NULL) ) {

        if ( bAddCRLF) {
            // add \r\n to the end of the string
            WriteFile (hFile, (LPCVOID) c_szCRLF,
                       2 * sizeof(WCHAR),
                       &dwBytesWritten,
                       NULL);
        }

        return(ERROR_SUCCESS);

    } else {

        return(GetLastError());
    }

}


//+--------------------------------------------------------------------------
//
//  Function:  ScepWcstrr
//
//  Synopsis:  Returns ptr to rightmost occurence of pSubstring in pString, NULL if none
//
//  Arguments: pString to look in, pSubstring to look for
//
//  Returns:   Returns ptr to rightmost occurence of pSubstring in pString, NULL if none
//
//+--------------------------------------------------------------------------
WCHAR *
ScepWcstrr(
    IN PWSTR pString,
    IN const WCHAR *pSubstring
    )
{
    int i, j, k;

    for (i = wcslen(pString) - wcslen(pSubstring) ; i >= 0; i-- ) {

        for (j = i, k = 0; pSubstring[k] != L'\0' && towlower(pString[j]) == towlower(pSubstring[k]); j++, k++)
            ;

        if ( k > 0 && pSubstring[k] == L'\0')

            return pString + i;
    }

    return NULL;

}

DWORD
ScepExpandEnvironmentVariable(
   IN PWSTR oldFileName,
   IN PCWSTR szEnv,
   IN DWORD nFlag,
   OUT PWSTR *newFileName)
/*
Description:

    Expand built-in environment variables known by SCE, including %SystemRoot%,
    %SystemDirectory%, %SystemDrive%, %DSDIT%, %DSLOG%, %SYSVOL%, %BOOTDRIVE%.

Parameters:

    oldFileName - the file name to expand

    szEnv       - the environment variable to search for

    nFlag       - the corresponding system env variable flag
                      SCE_FLAG_WINDOWS_DIR
                      SCE_FLAG_SYSTEM_DIR
                      SCE_FLAG_BOOT_DRIVE
                      SCE_FLAG_DSDIT_DIR
                      SCE_FLAG_DSLOG_DIR
                      SCE_FLAG_SYSVOL_DIR

    newFileName - the expanded file name if succeeded

Return Value:

    ERROR_FILE_NOT_FOUND if the environment varialbe is not found in the input file name

    ERROR_SUCCESS if the env variable is successfully expanded

    Otherwise, error code is returned.

*/
{
    if ( oldFileName == NULL || szEnv == NULL || newFileName == NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    PWSTR pTemp = wcsstr( _wcsupr(oldFileName), szEnv);
    LPTSTR  NtDir=NULL;
    DWORD   newFileSize, dSize=0;
    DWORD rc = ERROR_FILE_NOT_FOUND;

    if ( pTemp != NULL ) {
        //
        // found the environment variable
        //
        rc = ScepGetNTDirectory( &NtDir, &dSize, nFlag );

        if ( NO_ERROR == rc && NtDir ) {

            pTemp += wcslen(szEnv);
            BOOL bSysDrive=FALSE;

            switch ( nFlag ) {
            case SCE_FLAG_WINDOWS_DIR:
                if ( _wcsicmp(szEnv, L"%SYSTEMDRIVE%") == 0 ) {
                    dSize = 3;
                    bSysDrive = TRUE;
                }
                break;
            case SCE_FLAG_BOOT_DRIVE:
                if ( *pTemp == L'\\' ) pTemp++;  // NtDir contains the back slash already
                break;
            }

            newFileSize = dSize + wcslen(pTemp) + 1;
            *newFileName = (PWSTR)ScepAlloc( LMEM_ZEROINIT, newFileSize*sizeof(TCHAR));

            if (*newFileName != NULL) {

               if ( SCE_FLAG_WINDOWS_DIR == nFlag && bSysDrive ) {

                   // system drive letter
                   **newFileName = NtDir[0];
                   if ( pTemp[0] )
                       swprintf(*newFileName+1, L":%s", _wcsupr(pTemp));
                   else
                       swprintf(*newFileName+1, L":\\");

               } else {
                   swprintf(*newFileName, L"%s%s", NtDir, _wcsupr(pTemp));
               }

            }
            else
               rc = ERROR_NOT_ENOUGH_MEMORY;

        } else if ( NO_ERROR == rc && !NtDir ) {
            rc = ERROR_NOT_ENOUGH_MEMORY;
        }

        if ( NtDir ) {
            ScepFree(NtDir);
        }
    }

    return(rc);
}


DWORD
ScepEnforcePolicyPropagation()
{

    DWORD rc;
    HKEY hKey1=NULL;
    HKEY hKey=NULL;
    DWORD RegType;
    DWORD dwInterval=0;
    DWORD DataSize=sizeof(DWORD);

    if(( rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                           GPT_SCEDLL_NEW_PATH,
                          0,
                          KEY_READ | KEY_WRITE,
                          &hKey
                         )) == ERROR_SUCCESS ) {

        rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                              SCE_ROOT_PATH,
                              0,
                              KEY_READ | KEY_WRITE,
                              &hKey1
                             );
    }

    if ( ERROR_SUCCESS == rc ) {

        if ( ERROR_SUCCESS != RegQueryValueEx(hKey1,
                                     TEXT("GPOSavedInterval"),
                                     0,
                                     &RegType,
                                     (BYTE *)&dwInterval,
                                     &DataSize
                                    ) ) {
            //
            // either the value doesn't exist or fail to read it.
            // In either case, it's considered as no backup value
            // Now query the current value and save it
            //
            DataSize = sizeof(DWORD);
            if ( ERROR_SUCCESS != RegQueryValueEx(hKey,
                                         TEXT("MaxNoGPOListChangesInterval"),
                                         0,
                                         &RegType,
                                         (BYTE *)&dwInterval,
                                         &DataSize
                                        ) ) {
                dwInterval = 960;
            }

            rc = RegSetValueEx( hKey1,
                                TEXT("GPOSavedInterval"),
                                0,
                                REG_DWORD,
                                (BYTE *)&dwInterval,
                                sizeof(DWORD)
                                );

        } // else if the value already exists, don't need to save it again



        if ( ERROR_SUCCESS == rc ) {
            dwInterval = 1;
            rc = RegSetValueEx( hKey,
                                TEXT("MaxNoGPOListChangesInterval"),
                                0,
                                REG_DWORD,
                                (BYTE *)&dwInterval,
                                sizeof(DWORD)
                                );
        }

    }

    //
    // close the keys
    //
    if ( hKey1 )
        RegCloseKey( hKey1 );

    if ( hKey )
        RegCloseKey( hKey );

    return(rc);

}

DWORD
ScepGetTimeStampString(
    IN OUT PWSTR pvBuffer
    )
/*
Retrun long format of date/time string based on the locale.
*/
{
    if ( pvBuffer == NULL ) {
        return(ERROR_INVALID_PARAMETER);
    }

    DWORD         rc;
    LARGE_INTEGER CurrentTime;
    LARGE_INTEGER SysTime;
    TIME_FIELDS   TimeFields;
    NTSTATUS      NtStatus;

    FILETIME      ft;
    SYSTEMTIME    st;

    NtStatus = NtQuerySystemTime(&SysTime);
    rc = RtlNtStatusToDosError(NtStatus);

    RtlSystemTimeToLocalTime (&SysTime,&CurrentTime);

    if ( NT_SUCCESS(NtStatus) &&
         (CurrentTime.LowPart != 0 || CurrentTime.HighPart != 0) ) {

        rc = ERROR_SUCCESS;

        ft.dwLowDateTime = CurrentTime.LowPart;
        ft.dwHighDateTime = CurrentTime.HighPart;

        if ( !FileTimeToSystemTime(&ft, &st) ) {

            rc = GetLastError();

        } else {
            //
            // format date/time into the right locale format
            //

            TCHAR szDate[32];
            TCHAR szTime[32];

            //
            // GetDateFormat is the NLS routine that formats a time in a
            // locale-sensitive fashion.
            //
            if (0 == GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE,
                                    &st, NULL,szDate, 32)) {
                rc = GetLastError();

            } else {
                //
                // GetTimeFormat is the NLS routine that formats a time in a
                // locale-sensitive fashion.
                //
                if (0 == GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &st, NULL, szTime, 32)) {

                    rc = GetLastError();

                } else {

                    //
                    // Concatenate date and time
                    //
                    wcscpy(pvBuffer, szDate);
                    wcscat(pvBuffer, L" ");
                    wcscat(pvBuffer, szTime);

                }
            }
        }

        //
        // if can't get the system time in right locale,
        // print it in the current (default) format
        //
        if ( rc != NO_ERROR ) {

            memset(&TimeFields, 0, sizeof(TIME_FIELDS));

            RtlTimeToTimeFields (
                        &CurrentTime,
                        &TimeFields
                        );
            if ( TimeFields.Month > 0 && TimeFields.Month <= 12 &&
                 TimeFields.Day > 0 && TimeFields.Day <= 31 &&
                 TimeFields.Year > 1600 ) {

                swprintf(pvBuffer, L"%02d/%02d/%04d %02d:%02d:%02d\0",
                                 TimeFields.Month, TimeFields.Day, TimeFields.Year,
                                 TimeFields.Hour, TimeFields.Minute, TimeFields.Second);
            } else {
                swprintf(pvBuffer, L"%08x08x\0", CurrentTime.HighPart, CurrentTime.LowPart);
            }
        }

        rc = ERROR_SUCCESS;
    }

    return(rc);
}

DWORD
ScepAppendCreateMultiSzRegValue(
    IN  HKEY    hKeyRoot,
    IN  PWSTR   pszSubKey,
    IN  PWSTR   pszValueName,
    IN  PWSTR   pszValueValue
    )
/*++

Routine Description:

    This routine will append(if existing)/create(if not existing) w.r.t. MULTI_SZ values

Arguments:

    hKeyRoot        - root such as HKEY_LOCAL_MACHINE
    pszSubKey       - subkey such as "Software\\Microsoft\\Windows NT\\CurrentVersion\\SeCEdit"
    pszValueName    - value name of the key to be changed
    pszValueValue   - value of the value name to be changed


Return:

    error code (DWORD)
--*/
{

    DWORD   rc = ERROR_SUCCESS;
    DWORD   dwSize = 0;
    HKEY    hKey = NULL;
    DWORD   dwNewKey = NULL;
    DWORD   dwRegType = 0;

    if (hKeyRoot == NULL || pszSubKey  == NULL || pszValueName  == NULL || pszValueValue == NULL) {

        return ERROR_INVALID_PARAMETER;

    }

    if(( rc = RegOpenKeyEx(hKeyRoot,
                              pszSubKey,
                              0,
                              KEY_SET_VALUE | KEY_QUERY_VALUE ,
                              &hKey
                             )) != ERROR_SUCCESS ) {

        rc = RegCreateKeyEx(
                   hKeyRoot,
                   pszSubKey,
                   0,
                   NULL,
                   REG_OPTION_NON_VOLATILE,
                   KEY_WRITE,
                   NULL,
                   &hKey,
                   &dwNewKey
                  );
    }

    if ( ERROR_SUCCESS == rc ) {

        //
        // need to read the MULTI_SZ, append to it and set then new MULTI_SZ value
        //

        rc = RegQueryValueEx(hKey,
                             pszValueName,
                             0,
                             &dwRegType,
                             NULL,
                             &dwSize
                            );

        if ( ERROR_SUCCESS == rc || ERROR_FILE_NOT_FOUND == rc ) {

            //
            // dwSize is always size in bytes
            //

            DWORD   dwBytesToAdd = 0;

            //
            // if dwUnicodeSize == 0, then MULTI_SZ value was non-existent before
            //

            DWORD   dwUnicodeSize = (dwSize >= 2 ? dwSize/2 - 1 : 0);

            dwBytesToAdd = 2 * (wcslen(pszValueValue) + 2);

            PWSTR pszValue = (PWSTR)ScepAlloc( LMEM_ZEROINIT, dwSize + dwBytesToAdd) ;

            if ( pszValue != NULL ) {

                rc = RegQueryValueEx(hKey,
                                     pszValueName,
                                     0,
                                     &dwRegType,
                                     (BYTE *)pszValue,
                                     &dwSize
                                    );

                //
                // append pszValueValue to the end of the MULTI_SZ taking care of duplicates
                // i.e. abc\0def\0ghi\0\0 to something like
                //      abc\0def\0ghi\0jkl\0\0
                //

                if ( ScepMultiSzWcsstr(pszValue, pszValueValue) == NULL ) {

                    memcpy(pszValue + dwUnicodeSize, pszValueValue, dwBytesToAdd);
                    memset(pszValue + dwUnicodeSize + (dwBytesToAdd/2 - 2), '\0', 4);

                    if ( ERROR_SUCCESS == rc  || ERROR_FILE_NOT_FOUND == rc) {

                        rc = RegSetValueEx( hKey,
                                            pszValueName,
                                            0,
                                            REG_MULTI_SZ,
                                            (BYTE *)pszValue,
                                            (dwUnicodeSize == 0 ? dwSize + dwBytesToAdd : dwSize + dwBytesToAdd - 2)
                                          );

                    }
                }

                ScepFree(pszValue);
            }

            else {

                rc = ERROR_NOT_ENOUGH_MEMORY;
            }
        }

    }

    if( hKey )
        RegCloseKey( hKey );

    return rc;

}

PWSTR
ScepMultiSzWcsstr(
    PWSTR   pszStringToSearchIn,
    PWSTR   pszStringToSearchFor
    )
/*++

Routine Description:

    MULTI_SZ version of wcsstr

Arguments:

    pszStringToSearchIn     -   \0\0 terminated string to search in (MULTI_SZ)
    pszStringToSearchFor    -   \0 terminated string to search for (regular unicode string)

Return:

    pointer to first occurence of pszStringToSearchFor in pszStringToSearchIn

--*/
{
    PWSTR   pszCurrString = NULL;

    if (pszStringToSearchIn == NULL || pszStringToSearchFor == NULL) {
        return NULL;
    }

    if (pszStringToSearchFor[0] == L'\0' ||
        (pszStringToSearchIn[0] == L'\0' && pszStringToSearchIn[1] == L'\0') ) {
        return NULL;
    }

    pszCurrString = pszStringToSearchIn;

    __try {

        while ( !(pszCurrString[0] == L'\0' &&  pszCurrString[1] == L'\0') ) {

            if ( NULL != wcsstr(pszCurrString, pszStringToSearchFor) ) {
                return pszCurrString;
            }

            //
            // so, if C:\0E:\0\0, advance pszCurrString to the first \0 at the end ie. C:\0E:\0\0
            //        ^                                                                  ^

            pszCurrString += wcslen(pszCurrString) ;

            if (pszCurrString[0] == L'\0' &&  pszCurrString[1] == L'\0') {
                return NULL;
            }

            //
            // if it stopped at C:\0E:\0\0, advance pszCurrString C:\0E:\0\0
            //                     ^                                  ^

            pszCurrString += 1;
        }

    } __except(EXCEPTION_EXECUTE_HANDLER) {
    }


    return NULL;
}



DWORD
ScepEscapeString(
    IN const PWSTR pszSource,
    IN const DWORD dwSourceChars,
    IN const WCHAR wcEscapee,
    IN const WCHAR wcEscaper,
    IN OUT PWSTR pszTarget
    )

/* ++

Routine Description:

   Escapes escapee with escaper i.e.

   escapee -> escaper escapee escaper

   e.g. a,\0b\0c\0\0 -> a","\0b\0c\0\0

Arguments:

    pszSource       -   The source string

    dwSourceChars   -   The number of chars in pszSource

    wcEscapee       -  The escapee

    wcEscaper       -   The escaper

    pszTarget       -   The destination string

Return value:

   Number of characters copied to the target

-- */
{

    DWORD   dwTargetChars = 0;

    for (DWORD dwIndex=0; dwIndex < dwSourceChars; dwIndex++) {

        if ( pszSource[dwIndex] == wcEscapee ){
            pszTarget[0] = wcEscaper;
            pszTarget[1] = wcEscapee;
            pszTarget[2] = wcEscaper;
            pszTarget += 3;
            dwTargetChars +=3;
        }
        else {
            pszTarget[0] = pszSource[dwIndex];
            pszTarget++;
            ++dwTargetChars ;
        }
    }

    return dwTargetChars;
}