/*++

Copyright (c) 1999-2000  Microsoft Corporation

Module Name:

    Helper.cpp

Abstract:

    Various funtion encapsulate HELP user account
    validation, creating.

Author:

    HueiWang    2/17/2000

--*/

#include "stdafx.h"
#include <time.h>
#include <stdio.h>

#ifndef __WIN9XBUILD__

#include <windows.h>
#include <ntsecapi.h>
#include <lmcons.h>
#include <lm.h>
#include <sspi.h>
#include <wtsapi32.h>
#include <winbase.h>
#include <security.h>


#endif

#include "Helper.h"

#ifndef __WIN9XBUILD__

#if DBG

void
DebugPrintf(
    IN LPCTSTR format, ...
    )
/*++

Routine Description:

    sprintf() like wrapper around OutputDebugString().

Parameters:

    hConsole : Handle to console.
    format : format string.

Returns:

    None.

Note:

    To be replace by generic tracing code.

++*/
{
    TCHAR  buf[8096];   // max. error text
    DWORD  dump;
    va_list marker;
    va_start(marker, format);

    SYSTEMTIME sysTime;
    GetSystemTime(&sysTime);

    try {

        memset(
                buf, 
                0, 
                sizeof(buf)
            );

        _sntprintf(
                buf,
                sizeof(buf)/sizeof(buf[0]),
                _TEXT(" %d [%d:%d:%d:%d:%d.%d] : "),
                GetCurrentThreadId(),
                sysTime.wMonth,
                sysTime.wDay,
                sysTime.wHour,
                sysTime.wMinute,
                sysTime.wSecond,
                sysTime.wMilliseconds
            );

        _vsntprintf(
                buf + lstrlen(buf),
                sizeof(buf)/sizeof(buf[0]) - lstrlen(buf),
                format,
                marker
            );

        OutputDebugString(buf);
    }
    catch(...) {
    }

    va_end(marker);
    return;
}
#endif

#endif


void
UnixTimeToFileTime(
    time_t t,
    LPFILETIME pft
    )
{
    LARGE_INTEGER li;

    li.QuadPart = Int32x32To64(t, 10000000) + 116444736000000000;

    pft->dwHighDateTime = li.HighPart;
    pft->dwLowDateTime = li.LowPart;
}


#ifndef __WIN9XBUILD__

/*----------------------------------------------------------------------------
Routine Description:

    This function checks to see whether the specified sid is enabled in
    the specified token.

Arguments:

    TokenHandle - If present, this token is checked for the sid. If not
        present then the current effective token will be used. This must
        be an impersonation token.

    SidToCheck - The sid to check for presence in the token

    IsMember - If the sid is enabled in the token, contains TRUE otherwise
        false.

Return Value:

    TRUE - The API completed successfully. It does not indicate that the
        sid is a member of the token.

    FALSE - The API failed. A more detailed status code can be retrieved
        via GetLastError()


Note : Code modified from 5.0 \\rastaman\ntwin\src\base\advapi\security.c
----------------------------------------------------------------------------*/
BOOL
TLSCheckTokenMembership(
    IN HANDLE TokenHandle OPTIONAL,
    IN PSID SidToCheck,
    OUT PBOOL IsMember
    )
{
    HANDLE ProcessToken = NULL;
    HANDLE EffectiveToken = NULL;
    DWORD  Status = ERROR_SUCCESS;
    PISECURITY_DESCRIPTOR SecDesc = NULL;
    ULONG SecurityDescriptorSize;
    GENERIC_MAPPING GenericMapping = { STANDARD_RIGHTS_READ,
                                       STANDARD_RIGHTS_EXECUTE,
                                       STANDARD_RIGHTS_WRITE,
                                       STANDARD_RIGHTS_ALL };
    //
    // The size of the privilege set needs to contain the set itself plus
    // any privileges that may be used. The privileges that are used
    // are SeTakeOwnership and SeSecurity, plus one for good measure
    //
    BYTE PrivilegeSetBuffer[sizeof(PRIVILEGE_SET) + 3*sizeof(LUID_AND_ATTRIBUTES)];
    PPRIVILEGE_SET PrivilegeSet = (PPRIVILEGE_SET) PrivilegeSetBuffer;
    ULONG PrivilegeSetLength = sizeof(PrivilegeSetBuffer);
    ACCESS_MASK AccessGranted = 0;
    BOOL AccessStatus = FALSE;
    PACL Dacl = NULL;

    #define MEMBER_ACCESS 1

    *IsMember = FALSE;

    //
    // Get a handle to the token
    //
    if (TokenHandle != NULL)
    {
        EffectiveToken = TokenHandle;
    }
    else
    {
        if(!OpenThreadToken(GetCurrentThread(),
                            TOKEN_QUERY,
                            FALSE,              // don't open as self
                            &EffectiveToken))
        {
            //
            // if there is no thread token, try the process token
            //
            if((Status=GetLastError()) == ERROR_NO_TOKEN)
            {
                if(!OpenProcessToken(GetCurrentProcess(),
                                     TOKEN_QUERY | TOKEN_DUPLICATE,
                                     &ProcessToken))
                {
                    Status = GetLastError();
                }

                //
                // If we have a process token, we need to convert it to an
                // impersonation token
                //
                if (Status == ERROR_SUCCESS)
                {
                    BOOL Result;
                    Result = DuplicateToken(ProcessToken,
                                            SecurityImpersonation,
                                            &EffectiveToken);
                    CloseHandle(ProcessToken);
                    if (!Result)
                    {
                        return(FALSE);
                    }
                }
            }

            if (Status != ERROR_SUCCESS)
            {
                goto Cleanup;
            }
        }
    }

    //
    // Construct a security descriptor to pass to access check
    //

    //
    // The size is equal to the size of an SD + twice the length of the SID
    // (for owner and group) + size of the DACL = sizeof ACL + size of the
    // ACE, which is an ACE + length of
    // ths SID.
    //

    SecurityDescriptorSize = sizeof(SECURITY_DESCRIPTOR) +
                                sizeof(ACCESS_ALLOWED_ACE) +
                                sizeof(ACL) +
                                3 * GetLengthSid(SidToCheck);

    SecDesc = (PISECURITY_DESCRIPTOR) LocalAlloc(LMEM_ZEROINIT, SecurityDescriptorSize );
    if (SecDesc == NULL)
    {
        Status = ERROR_OUTOFMEMORY;
        goto Cleanup;
    }
    Dacl = (PACL) (SecDesc + 1);

    InitializeSecurityDescriptor(SecDesc, SECURITY_DESCRIPTOR_REVISION);

    //
    // Fill in fields of security descriptor
    //
    SetSecurityDescriptorOwner(SecDesc, SidToCheck, FALSE);
    SetSecurityDescriptorGroup(SecDesc, SidToCheck, FALSE);

    if(!InitializeAcl(  Dacl,
                        SecurityDescriptorSize - sizeof(SECURITY_DESCRIPTOR),
                        ACL_REVISION))
    {
        Status=GetLastError();
        goto Cleanup;
    }

    if(!AddAccessAllowedAce(Dacl, ACL_REVISION, MEMBER_ACCESS, SidToCheck))
    {
        Status=GetLastError();  
        goto Cleanup;
    }

    if(!SetSecurityDescriptorDacl(SecDesc, TRUE, Dacl, FALSE))
    {
        Status=GetLastError();
        goto Cleanup;
    }

    if(!AccessCheck(SecDesc,
                    EffectiveToken,
                    MEMBER_ACCESS,
                    &GenericMapping,
                    PrivilegeSet,
                    &PrivilegeSetLength,
                    &AccessGranted,
                    &AccessStatus))
    {
        Status=GetLastError();
        goto Cleanup;
    }

    //
    // if the access check failed, then the sid is not a member of the
    // token
    //
    if ((AccessStatus == TRUE) && (AccessGranted == MEMBER_ACCESS))
    {
        *IsMember = TRUE;
    }


Cleanup:
    if (TokenHandle == NULL && EffectiveToken != NULL)
    {
        CloseHandle(EffectiveToken);
    }

    if (SecDesc != NULL)
    {
        LocalFree(SecDesc);
    }

    return (Status == ERROR_SUCCESS) ? TRUE : FALSE;
}


/*------------------------------------------------------------------------

 BOOL IsUserAdmin(BOOL)

  returns TRUE if user is an admin
          FALSE if user is not an admin
------------------------------------------------------------------------*/
DWORD 
IsUserAdmin(
    BOOL* bMember
    )
{
    PSID psidAdministrators;
    SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
    DWORD dwStatus=ERROR_SUCCESS;

    do {
        if(!AllocateAndInitializeSid(&siaNtAuthority, 
                                     2, 
                                     SECURITY_BUILTIN_DOMAIN_RID,
                                     DOMAIN_ALIAS_RID_ADMINS,
                                     0, 0, 0, 0, 0, 0,
                                     &psidAdministrators))
        {
            dwStatus=GetLastError();
            continue;
        }

        // assume that we don't find the admin SID.
        if(!TLSCheckTokenMembership(NULL,
                                   psidAdministrators,
                                   bMember))
        {
            dwStatus=GetLastError();
        }

        FreeSid(psidAdministrators);

    } while(FALSE);

    return dwStatus;
}

#endif

DWORD
GetRandomNumber( 
    HCRYPTPROV hProv,
    DWORD* pdwRandom
    )
/*++

--*/
{
    DWORD dwStatus = ERROR_SUCCESS;
    
    if( NULL == hProv )
    {
        dwStatus = ERROR_INVALID_PARAMETER;
    }
    else 
    {
        if( !CryptGenRandom(hProv, sizeof(*pdwRandom), (PBYTE)pdwRandom) )
        {
            dwStatus = GetLastError();
        }
    }

    MYASSERT( ERROR_SUCCESS == dwStatus );
    return dwStatus; 
}

//-----------------------------------------------------

DWORD
ShuffleCharArray(
    IN HCRYPTPROV hProv,
    IN int iSizeOfTheArray,
    IN OUT TCHAR *lptsTheArray
    )
/*++

Routine Description:

    Random shuffle content of a char. array.

Parameters:

    iSizeOfTheArray : Size of array.
    lptsTheArray : On input, the array to be randomly shuffer,
                   on output, the shuffled array.

Returns:

    None.
                   
Note:

    Code Modified from winsta\server\wstrpc.c

--*/
{
    int i;
    int iTotal;
    DWORD dwStatus = ERROR_SUCCESS;

    if( NULL == hProv )
    {
        dwStatus = ERROR_INVALID_PARAMETER;
    }
    else
    {
        iTotal = iSizeOfTheArray / sizeof(TCHAR);
        for (i = 0; i < iTotal && ERROR_SUCCESS == dwStatus; i++)
        {
            DWORD RandomNum;
            TCHAR c;

            dwStatus = GetRandomNumber(hProv, &RandomNum);

            if( ERROR_SUCCESS == dwStatus )
            {
                c = lptsTheArray[i];
                lptsTheArray[i] = lptsTheArray[RandomNum % iTotal];
                lptsTheArray[RandomNum % iTotal] = c;
            }
        }
    }

    return dwStatus;
}

//-----------------------------------------------------

DWORD
GenerateRandomBytes(
    IN DWORD dwSize,
    IN OUT LPBYTE pbBuffer
    )
/*++

Description:

    Generate fill buffer with random bytes.

Parameters:

    dwSize : Size of buffer pbBuffer point to.
    pbBuffer : Pointer to buffer to hold the random bytes.

Returns:

    TRUE/FALSE

--*/
{
    HCRYPTPROV hProv = NULL;
    DWORD dwStatus = ERROR_SUCCESS;

    //
    // Create a Crypto Provider to generate random number
    //
    if( !CryptAcquireContext(
                    &hProv,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_VERIFYCONTEXT
                ) )
    {
        dwStatus = GetLastError();
        goto CLEANUPANDEXIT;
    }

    if( !CryptGenRandom(hProv, dwSize, pbBuffer) )
    {
        dwStatus = GetLastError();
    }

CLEANUPANDEXIT:    

    if( NULL != hProv )
    {
        CryptReleaseContext( hProv, 0 );
    }

    return dwStatus;
}


DWORD
GenerateRandomString(
    IN DWORD dwSizeRandomSeed,
    IN OUT LPTSTR* pszRandomString
    )
/*++


--*/
{
    PBYTE lpBuffer = NULL;
    DWORD dwStatus = ERROR_SUCCESS;
    BOOL bSuccess;
    DWORD cbConvertString = 0;

    if( 0 == dwSizeRandomSeed || NULL == pszRandomString )
    {
        dwStatus = ERROR_INVALID_PARAMETER;
        MYASSERT(FALSE);
        goto CLEANUPANDEXIT;
    }

    *pszRandomString = NULL;

    lpBuffer = (PBYTE)LocalAlloc( LPTR, dwSizeRandomSeed );  
    if( NULL == lpBuffer )
    {
        dwStatus = GetLastError();
        goto CLEANUPANDEXIT;
    }

    dwStatus = GenerateRandomBytes( dwSizeRandomSeed, lpBuffer );

    if( ERROR_SUCCESS != dwStatus )
    {
        goto CLEANUPANDEXIT;
    }

    // Convert to string
    bSuccess = CryptBinaryToString(
                                lpBuffer,
                                dwSizeRandomSeed,
                                CRYPT_STRING_BASE64,
                                0,
                                &cbConvertString
                            );
    if( FALSE == bSuccess )
    {
        dwStatus = GetLastError();
        goto CLEANUPANDEXIT;
    }

    *pszRandomString = (LPTSTR)LocalAlloc( LPTR, (cbConvertString+1)*sizeof(TCHAR) );
    if( NULL == *pszRandomString )
    {
        dwStatus = GetLastError();
        goto CLEANUPANDEXIT;
    }

    bSuccess = CryptBinaryToString(
                                lpBuffer,
                                dwSizeRandomSeed,
                                CRYPT_STRING_BASE64,
                                *pszRandomString,
                                &cbConvertString
                            );
    if( FALSE == bSuccess )
    {
        dwStatus = GetLastError();
    }
    else
    {
        if( (*pszRandomString)[cbConvertString - 1] == '\n' &&
            (*pszRandomString)[cbConvertString - 2] == '\r' )
        {
            (*pszRandomString)[cbConvertString - 2] = 0;
        }
    }

CLEANUPANDEXIT:

    if( ERROR_SUCCESS != dwStatus )
    {
        if( NULL != *pszRandomString )
        {
            LocalFree(*pszRandomString);
        }
    }

    if( NULL != lpBuffer )
    {
        LocalFree(lpBuffer);
    }

    return dwStatus;
}

DWORD
CreatePassword(
    OUT TCHAR *pszPassword
    )
/*++

Routine Description:

    Routine to randomly create a password.

Parameters:

    pszPassword : Pointer to buffer to received a randomly generated
                  password, buffer must be at least 
                  MAX_HELPACCOUNT_PASSWORD+1 characters.

Returns:

    None.

Note:

    Code copied from winsta\server\wstrpc.c

--*/
{
    HCRYPTPROV hProv = NULL;

    int   nLength = MAX_HELPACCOUNT_PASSWORD;
    int   iTotal = 0;
    DWORD RandomNum = 0;
    int   i;
    time_t timeVal;
    DWORD dwStatus = ERROR_SUCCESS;


    TCHAR six2pr[64] = 
    {
        _T('A'), _T('B'), _T('C'), _T('D'), _T('E'), _T('F'), _T('G'),
        _T('H'), _T('I'), _T('J'), _T('K'), _T('L'), _T('M'), _T('N'),
        _T('O'), _T('P'), _T('Q'), _T('R'), _T('S'), _T('T'), _T('U'),
        _T('V'), _T('W'), _T('X'), _T('Y'), _T('Z'), _T('a'), _T('b'),
        _T('c'), _T('d'), _T('e'), _T('f'), _T('g'), _T('h'), _T('i'),
        _T('j'), _T('k'), _T('l'), _T('m'), _T('n'), _T('o'), _T('p'),
        _T('q'), _T('r'), _T('s'), _T('t'), _T('u'), _T('v'), _T('w'),
        _T('x'), _T('y'), _T('z'), _T('0'), _T('1'), _T('2'), _T('3'),
        _T('4'), _T('5'), _T('6'), _T('7'), _T('8'), _T('9'), _T('*'),
        _T('_')
    };

    TCHAR something1[12] = 
    {
        _T('!'), _T('@'), _T('#'), _T('$'), _T('^'), _T('&'), _T('*'),
        _T('('), _T(')'), _T('-'), _T('+'), _T('=')
    };

    TCHAR something2[10] = 
    {
        _T('0'), _T('1'), _T('2'), _T('3'), _T('4'), _T('5'), _T('6'),
        _T('7'), _T('8'), _T('9')
    };

    TCHAR something3[26] = 
    {
        _T('A'), _T('B'), _T('C'), _T('D'), _T('E'), _T('F'), _T('G'),
        _T('H'), _T('I'), _T('J'), _T('K'), _T('L'), _T('M'), _T('N'),
        _T('O'), _T('P'), _T('Q'), _T('R'), _T('S'), _T('T'), _T('U'),
        _T('V'), _T('W'), _T('X'), _T('Y'), _T('Z')
    };

    TCHAR something4[26] = 
    {
        _T('a'), _T('b'), _T('c'), _T('d'), _T('e'), _T('f'), _T('g'),
        _T('h'), _T('i'), _T('j'), _T('k'), _T('l'), _T('m'), _T('n'),
        _T('o'), _T('p'), _T('q'), _T('r'), _T('s'), _T('t'), _T('u'),
        _T('v'), _T('w'), _T('x'), _T('y'), _T('z')
    };

    //
    // Create a Crypto Provider to generate random number
    //
    if( !CryptAcquireContext(
                    &hProv,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_VERIFYCONTEXT
                ) )
    {
        dwStatus = GetLastError();
        goto CLEANUPANDEXIT;
    }

    //
    //  Shuffle around the six2pr[] array.
    //

    dwStatus = ShuffleCharArray(hProv, sizeof(six2pr), six2pr);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }


    //
    //  Assign each character of the password array.
    //

    iTotal = sizeof(six2pr) / sizeof(TCHAR);
    for (i=0; i<nLength && ERROR_SUCCESS == dwStatus; i++) 
    {
        dwStatus = GetRandomNumber(hProv, &RandomNum);
        if( ERROR_SUCCESS == dwStatus )
        {
            pszPassword[i]=six2pr[RandomNum%iTotal];
        }
    }

    if( ERROR_SUCCESS != dwStatus ) 
    {
        MYASSERT(FALSE);
        goto CLEANUPANDEXIT;
    }


    //
    //  In order to meet a possible policy set upon passwords, replace chars
    //  2 through 5 with these:
    //
    //  1) something from !@#$%^&*()-+=
    //  2) something from 1234567890
    //  3) an uppercase letter
    //  4) a lowercase letter
    //

    dwStatus = ShuffleCharArray(hProv, sizeof(something1), (TCHAR*)&something1);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    dwStatus = ShuffleCharArray(hProv, sizeof(something2), (TCHAR*)&something2);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    dwStatus = ShuffleCharArray(hProv, sizeof(something3), (TCHAR*)&something3);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    dwStatus = ShuffleCharArray(hProv, sizeof(something4), (TCHAR*)&something4);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }


    dwStatus = GetRandomNumber(hProv, &RandomNum);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    iTotal = sizeof(something1) / sizeof(TCHAR);
    pszPassword[2] = something1[RandomNum % iTotal];

    dwStatus = GetRandomNumber(hProv, &RandomNum);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    iTotal = sizeof(something2) / sizeof(TCHAR);
    pszPassword[3] = something2[RandomNum % iTotal];

    dwStatus = GetRandomNumber(hProv, &RandomNum);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    iTotal = sizeof(something3) / sizeof(TCHAR);
    pszPassword[4] = something3[RandomNum % iTotal];

    dwStatus = GetRandomNumber(hProv, &RandomNum);
    if( ERROR_SUCCESS != dwStatus ) 
    {
        goto CLEANUPANDEXIT;
    }

    iTotal = sizeof(something4) / sizeof(TCHAR);
    pszPassword[5] = something4[RandomNum % iTotal];

    pszPassword[nLength] = _T('\0');

CLEANUPANDEXIT:

    if( NULL != hProv )
    {
        CryptReleaseContext( hProv, 0 );
    }

    return dwStatus;
}

//--------------------------------------------------------
#ifndef __WIN9XBUILD__


BOOL
LookupAliasFromRid(
    LPWSTR pTargetComputer,
    DWORD Rid,
    LPWSTR pName,
    PDWORD cchName
    )
{
    BOOL fRet;
    PSID pSid = NULL;
    SID_IDENTIFIER_AUTHORITY SidIdentifierAuthority = SECURITY_NT_AUTHORITY;
    SID_NAME_USE SidNameUse;
    ULONG cchDomainName;
    WCHAR szDomainName[256];

    //
    //  Sid is the same regardless of machine, since the well-known
    //  BUILTIN domain is referenced.
    //

    fRet = AllocateAndInitializeSid(
                                &SidIdentifierAuthority,
                                2,
                                SECURITY_BUILTIN_DOMAIN_RID,
                                Rid,
                                0, 0, 0, 0, 0, 0,
                                &pSid
                            );

    if (fRet)
    {
        cchDomainName = sizeof(szDomainName)/sizeof(szDomainName);

        fRet = LookupAccountSidW(
                            pTargetComputer,
                            pSid,
                            pName,
                            cchName,
                            szDomainName,
                            &cchDomainName,
                            &SidNameUse
                        );

        FreeSid(pSid);
    }

    return(fRet);
}


DWORD
IsUserInLocalGroup(
    IN PBYTE pbUserSid,
    IN LPCTSTR pszLocalGroup,
    OUT BOOL* pbInGroup
    )
/*++

Routine Description:

    Check if user is member of specific local group.

Parameters:

    pbUserSid : SID of user to be verified.
    pszLocalGroup : Name of local group.
    pbInGroup : Return TRUE if user is in group, FALSE otherwise.

Returns:

    ERROR_SUCCESS or error code, membership is returned via pbInGroup
    parameter.
    
Note:

    If 'everyone' is member of the specfied group, routine will 
    immediately return SUCCESS.

--*/
{
    NET_API_STATUS netErr;
    LOCALGROUP_MEMBERS_INFO_0* pBuf=NULL;

    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    PSID pEveryoneSID = NULL;
    DWORD dwEntries;
    DWORD dwTotal;

    *pbInGroup = FALSE;
    //
    // By default add everyone to the group
    //
    if(AllocateAndInitializeSid(  &SIDAuthWorld, 1,
                                  SECURITY_WORLD_RID,
                                  0, 0, 0, 0, 0, 0, 0,
                                  &pEveryoneSID) )
    {
        //
        // Retrieve group member.
        //
        netErr = NetLocalGroupGetMembers(
                                    NULL,
                                    pszLocalGroup,
                                    0,
                                    (LPBYTE *)&pBuf,
                                    MAX_PREFERRED_LENGTH,
                                    &dwEntries,
                                    &dwTotal,
                                    NULL
                                );

        if( NERR_Success == netErr )
        {
            for(DWORD index=0; index < dwEntries; index++ )
            {
                if(TRUE == EqualSid( pEveryoneSID, pBuf[index].lgrmi0_sid) )
                {
                    *pbInGroup = TRUE;
                    break;
                }

                if(NULL != pbUserSid && TRUE == EqualSid( pbUserSid, pBuf[index].lgrmi0_sid) )
                {
                    *pbInGroup = TRUE;
                    break;
                }
            }

            NetApiBufferFree( pBuf );
        }

        FreeSid( pEveryoneSID );
    }
    else
    {
        netErr = GetLastError();
    }

    return netErr;
}

BOOL
IsLocalGroupExists(
    IN LPWSTR pszGroupName
    )
/*++

Routine Description:

    Verify if local group exist on machine.

Parameter:

    pszGroupName : Name of the group to be checked.

Returns:

    TRUE/FALSE

--*/
{
    LOCALGROUP_INFO_1* pGroupInfo1;
    NET_API_STATUS netStatus;
    BOOL bGroupExists;

    //
    // Check to see if group exists
    //
    netStatus = NetLocalGroupGetInfo(
                            NULL,
                            pszGroupName,
                            1,
                            (PBYTE *)&pGroupInfo1
                        );

    if( NERR_Success == netStatus )
    {
        NetApiBufferFree(pGroupInfo1);
    }
    else
    {
        SetLastError( netStatus );
    }

    return ( NERR_Success == netStatus );
}

//---------------------------------------------------------

DWORD
CreateLocalGroup(
    IN LPWSTR pszGroupName,
    IN LPWSTR pszGroupDesc,
    IN BOOL bAddEveryone
    )
/*++

Routine Description:

    Create a group on local machine.

Parameters:

    pszGroupName : Group name.
    pszGroupDesc : Group desc.
    bAddEveryone : TRUE if add 'everyone' to the group, FALSE
                   otherwise.

Returns:
    
    ERROR_SUCCESS or error code

--*/
{
    NET_API_STATUS netStatus;
    LOCALGROUP_INFO_1 GroupInfo;
    DWORD dwParmErr;

    GroupInfo.lgrpi1_name = pszGroupName;
    GroupInfo.lgrpi1_comment = pszGroupDesc;

    netStatus = NetLocalGroupAdd(
                            NULL,
                            1,
                            (LPBYTE)&GroupInfo,
                            &dwParmErr
                        );

    if( NERR_Success == netStatus )
    {
        if(FALSE == IsLocalGroupExists(pszGroupName))
        {
            // We have big problem
            netStatus = GetLastError();
        }
    }

    if( NERR_Success == netStatus && TRUE == bAddEveryone )
    {
        LOCALGROUP_MEMBERS_INFO_0 gmember;
        SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
        PSID pEveryoneSID = NULL;

        //
        // add everyone to the group
        //
        if(AllocateAndInitializeSid(  &SIDAuthWorld, 1,
                                  SECURITY_WORLD_RID,
                                  0, 0, 0, 0, 0, 0, 0,
                                  &pEveryoneSID) )
        {
            gmember.lgrmi0_sid = pEveryoneSID;

            bAddEveryone = NetLocalGroupAddMembers(
                                    NULL,
                                    pszGroupName,
                                    0,
                                    (PBYTE)&gmember,
                                    1
                                );

            if( ERROR_MEMBER_IN_ALIAS == netStatus )
            {
                // ignore this error
                netStatus = NERR_Success;
            }

            FreeSid( pEveryoneSID );
        }
    }

    return netStatus;
}

//---------------------------------------------------------

DWORD
RenameLocalAccount(
    IN LPWSTR pszOrgName,
    IN LPWSTR pszNewName
)
/*++

Routine Description:


Parameters:


Returns:

    ERROR_SUCCESS or error code.

--*/
{
    NET_API_STATUS err;
    USER_INFO_0 UserInfo;

    UserInfo.usri0_name = pszNewName;
    err = NetUserSetInfo(
                        NULL,
                        pszOrgName,
                        0,
                        (LPBYTE) &UserInfo,
                        NULL
                    );

    return err;
}

DWORD
UpdateLocalAccountFullnameAndDesc(
    IN LPWSTR pszAccOrgName,
    IN LPWSTR pszAccFullName,
    IN LPWSTR pszAccDesc
    )
/*++

Routine Description:

    Update account full name and description.

Parameters:

    pszAccName : Account name.
    pszAccFullName : new account full name.
    pszAccDesc : new account description.

Returns:    

    ERROR_SUCCESS or error code

--*/
{
    LPBYTE pbServer = NULL;
    BYTE *pBuffer;
    NET_API_STATUS netErr = NERR_Success;
    DWORD parm_err;

    netErr = NetUserGetInfo( 
                        NULL, 
                        pszAccOrgName, 
                        3, 
                        &pBuffer 
                    );

    if( NERR_Success == netErr )
    {
        USER_INFO_3 *lpui3 = (USER_INFO_3 *)pBuffer;

        lpui3->usri3_comment = pszAccDesc;
        lpui3->usri3_full_name = pszAccFullName;

        netErr = NetUserSetInfo(
                            NULL,
                            pszAccOrgName,
                            3,
                            (PBYTE)lpui3,
                            &parm_err
                        );

        NetApiBufferFree(pBuffer);
    }

    return netErr;
}

DWORD
IsLocalAccountEnabled(
    IN LPWSTR pszUserName,
    IN BOOL* pEnabled
    )
/*++

Routine Description:

    Check if local account enabled    

Parameters:

    pszUserName : Name of user account.
    pEnabled : Return TRUE is account is enabled, FALSE otherwise.

Returns:

    ERROR_SUCCESS or error code.

--*/
{
    DWORD dwResult;
    NET_API_STATUS err;
    LPBYTE pBuffer;
    USER_INFO_1 *pUserInfo;

    err = NetUserGetInfo(
                        NULL,
                        pszUserName,
                        1,
                        &pBuffer
                    );

    if( NERR_Success == err )
    {
        pUserInfo = (USER_INFO_1 *)pBuffer;

        if (pUserInfo != NULL)
        {
            if( pUserInfo->usri1_flags & UF_ACCOUNTDISABLE )
            {
                *pEnabled = FALSE;
            }
            else
            {
                *pEnabled = TRUE;
            }
        }

        NetApiBufferFree( pBuffer );
    }
    else if( NERR_UserNotFound == err )
    {
        *pEnabled = FALSE;
        //err = NERR_Success;
    }

    return err;
}

//---------------------------------------------------------

DWORD
EnableLocalAccount(
    IN LPWSTR pszUserName,
    IN BOOL bEnable
    )
/*++

Routine Description:

    Routine to enable/disable a local account.

Parameters:

    pszUserName : Name of user account.
    bEnable : TRUE if enabling account, FALSE if disabling account.

Returns:

    ERROR_SUCCESS or error code.

--*/
{
    DWORD dwResult;
    NET_API_STATUS err;
    LPBYTE pBuffer;
    USER_INFO_1 *pUserInfo;
    BOOL bChangeAccStatus = TRUE;

    err = NetUserGetInfo(
                        NULL,
                        pszUserName,
                        1,
                        &pBuffer
                    );

    if( NERR_Success == err )
    {
        pUserInfo = (USER_INFO_1 *)pBuffer;

        if(pUserInfo != NULL)
        {

            if( TRUE == bEnable && pUserInfo->usri1_flags & UF_ACCOUNTDISABLE )
            {
                pUserInfo->usri1_flags &= ~UF_ACCOUNTDISABLE;
            }
            else if( FALSE == bEnable && !(pUserInfo->usri1_flags & UF_ACCOUNTDISABLE) )
            {
                pUserInfo->usri1_flags |= UF_ACCOUNTDISABLE;
            }   
            else
            {
                bChangeAccStatus = FALSE;
            }

            if( TRUE == bChangeAccStatus )
            {
                err = NetUserSetInfo( 
                                NULL,
                                pszUserName,
                                1,
                                pBuffer,
                                &dwResult
                            );
            }
        }

        NetApiBufferFree( pBuffer );
    }

    return err;
}

//---------------------------------------------------------

BOOL
IsPersonalOrProMachine()
/*++

Routine Description:

    Check if machine is PER or PRO sku.

Parameters:

    None.

Return:

    TRUE/FALSE
--*/
{
    OSVERSIONINFOEX osVersionInfo;
    DWORDLONG dwlConditionMask = 0;
    BOOL bSuccess;

    ZeroMemory(
            &osVersionInfo, 
            sizeof(OSVERSIONINFOEX)
        );

    osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osVersionInfo.wProductType = VER_NT_SERVER;

    VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
    bSuccess= VerifyVersionInfo(
                            &osVersionInfo,
                            VER_PRODUCT_TYPE,
                            dwlConditionMask
                        );

    return !bSuccess;
}
    

DWORD
CreateLocalAccount(
    IN LPWSTR pszUserName,
    IN LPWSTR pszUserPwd,
    IN LPWSTR pszFullName,
    IN LPWSTR pszComment,
    IN LPWSTR pszGroup,
    IN LPWSTR pszScript,
    OUT BOOL* pbAccountExist
    )
/*++

Routine Description:

    Create an user account on local machine.

Parameters:

    pszUserName : Name of the user account.
    pszUserPwd : User account password.
    pszFullName : Account Full Name.
    pszComment : Account comment.
    pszGroup : Local group of the account.
    pbAccountExist ; Return TRUE if account already exists, FALSE otherwise.

Returns:

    ERROR_SUCCESS or error code.

--*/
{
    LPBYTE pbServer = NULL;
    BYTE *pBuffer;
    NET_API_STATUS netErr = NERR_Success;
    DWORD parm_err;
    DWORD dwStatus;

    netErr = NetUserGetInfo( 
                        NULL, 
                        pszUserName, 
                        3, 
                        &pBuffer 
                    );

    if( NERR_Success == netErr )
    {
        //
        // User account exists, if account is disabled,
        // enable it and change password
        //
        USER_INFO_3 *lpui3 = (USER_INFO_3 *)pBuffer;

        if( lpui3->usri3_flags & UF_ACCOUNTDISABLE ||
            lpui3->usri3_flags & UF_LOCKOUT )
        {
            // enable the account
            lpui3->usri3_flags &= ~ ~UF_LOCKOUT;;

            if( lpui3->usri3_flags & UF_ACCOUNTDISABLE )
            {
                // we only reset password if account is disabled.
                lpui3->usri3_flags &= ~ UF_ACCOUNTDISABLE;
            }

            //lpui3->usri3_password = pszUserPwd;

            // reset password if account is disabled.
            lpui3->usri3_name = pszUserName;
            lpui3->usri3_comment = pszComment;
            lpui3->usri3_full_name = pszFullName;
            //lpui3->usri3_primary_group_id = dwGroupId;

            netErr = NetUserSetInfo(
                                NULL,
                                pszUserName,
                                3,
                                (PBYTE)lpui3,
                                &parm_err
                            );
        }

        *pbAccountExist = TRUE;
        NetApiBufferFree(pBuffer);
    }
    else if( NERR_UserNotFound == netErr )
    {
        //
        // Account does not exist, create and set it to our group
        //
        USER_INFO_1 UserInfo;

        memset(&UserInfo, 0, sizeof(USER_INFO_1));

        UserInfo.usri1_name = pszUserName;
        UserInfo.usri1_password = pszUserPwd;
        UserInfo.usri1_priv = USER_PRIV_USER;   // see USER_INFO_1 for detail
        UserInfo.usri1_comment = pszComment;
        UserInfo.usri1_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD;

        netErr = NetUserAdd(
                        NULL,
                        1,
                        (PBYTE)&UserInfo,
                        &parm_err
                    );

        *pbAccountExist = FALSE;
    }

    return netErr;
}

///////////////////////////////////////////////////////////////////////////////
DWORD
ChangeLocalAccountPassword(
    IN LPWSTR pszAccName,
    IN LPWSTR pszOldPwd,
    IN LPWSTR pszNewPwd
    )
/*++

Routine Description:

    Change password of a local account.

Parameters:

    pszAccName : Name of user account.
    pszOldPwd : Old password.
    pszNewPwd : New password.

Returns:

    ERROR_SUCCESS or error code.

Notes:

    User NetUserChangePassword(), must have priviledge

--*/
{
    USER_INFO_1003  sUserInfo3;
    NET_API_STATUS  netErr;


    UNREFERENCED_PARAMETER( pszOldPwd );

    sUserInfo3.usri1003_password = pszNewPwd;
    netErr = NetUserSetInfo( 
                        NULL,
                        pszAccName,
                        1003,
                        (BYTE *) &sUserInfo3,
                        0 
                    );

    return netErr;
}
   
///////////////////////////////////////////////////////////////////////////////
DWORD
RetrieveKeyFromLSA(
    IN PWCHAR pwszKeyName,
    OUT PBYTE * ppbKey,
    OUT DWORD * pcbKey 
    )
/*++

Routine Description:

    Retrieve private data previously stored with StoreKeyWithLSA().

Parameters:

    pwszKeyName : Name of the key.
    ppbKey : Pointer to PBYTE to receive binary data.
    pcbKey : Size of binary data.

Returns:

    ERROR_SUCCESS
    ERROR_INVALID_PARAMETER.
    ERROR_FILE_NOT_FOUND
    LSA return code

Note:

    Memory is allocated using LocalAlloc() 

--*/
{
    LSA_HANDLE PolicyHandle;
    UNICODE_STRING SecretKeyName;
    UNICODE_STRING *pSecretData;
    DWORD Status;

    if( ( NULL == pwszKeyName ) || ( NULL == ppbKey ) || ( NULL == pcbKey ) )
    {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // setup the UNICODE_STRINGs for the call.
    //
    InitLsaString( 
            &SecretKeyName, 
            pwszKeyName 
        );

    Status = OpenPolicy( 
                    NULL, 
                    POLICY_GET_PRIVATE_INFORMATION, 
                    &PolicyHandle 
                );

    if( Status != ERROR_SUCCESS )
    {
        return LsaNtStatusToWinError(Status);
    }

    Status = LsaRetrievePrivateData(
                            PolicyHandle,
                            &SecretKeyName,
                            &pSecretData
                        );

    LsaClose( PolicyHandle );

    if( Status != ERROR_SUCCESS )
    {
        return LsaNtStatusToWinError(Status);
    }

    if(pSecretData->Length)
    {
        *ppbKey = ( LPBYTE )LocalAlloc( LPTR, pSecretData->Length );

        if( *ppbKey )
        {
            *pcbKey = pSecretData->Length;
            CopyMemory( *ppbKey, pSecretData->Buffer, pSecretData->Length );
            Status = ERROR_SUCCESS;
        } 
        else 
        {
            Status = GetLastError();
        }
    }
    else
    {
        Status = ERROR_FILE_NOT_FOUND;
        *pcbKey = 0;
        *ppbKey = NULL;
    }

    ZeroMemory( pSecretData->Buffer, pSecretData->Length );
    LsaFreeMemory( pSecretData );

    return Status;
}

///////////////////////////////////////////////////////////////////////////////
DWORD
StoreKeyWithLSA(
    IN PWCHAR  pwszKeyName,
    IN BYTE *  pbKey,
    IN DWORD   cbKey 
    )
/*++

Routine Description:

    Save private data to LSA.

Parameters:

    pwszKeyName : Name of the key this data going to be stored under.
    pbKey : Binary data to be saved.
    cbKey : Size of binary data.

Returns:

    ERROR_SUCCESS
    ERROR_INVALID_PARAMETER.
    LSA return code

--*/
{
    LSA_HANDLE PolicyHandle;
    UNICODE_STRING SecretKeyName;
    UNICODE_STRING SecretData;
    DWORD Status;
    
    if( ( NULL == pwszKeyName ) )
    {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // setup the UNICODE_STRINGs for the call.
    //
    
    InitLsaString( 
            &SecretKeyName, 
            pwszKeyName 
        );

    SecretData.Buffer = ( LPWSTR )pbKey;
    SecretData.Length = ( USHORT )cbKey;
    SecretData.MaximumLength = ( USHORT )cbKey;

    Status = OpenPolicy( 
                    NULL, 
                    POLICY_CREATE_SECRET, 
                    &PolicyHandle 
                );

    if( Status != ERROR_SUCCESS )
    {
        return LsaNtStatusToWinError(Status);
    }

    Status = LsaStorePrivateData(
                PolicyHandle,
                &SecretKeyName,
                &SecretData
                );

    LsaClose(PolicyHandle);

    return LsaNtStatusToWinError(Status);
}


///////////////////////////////////////////////////////////////////////////////
DWORD
OpenPolicy(
    IN LPWSTR ServerName,
    IN DWORD  DesiredAccess,
    OUT PLSA_HANDLE PolicyHandle 
    )
/*++

Routine Description:

    Create/return a LSA policy handle.

Parameters:
    
    ServerName : Name of server, refer to LsaOpenPolicy().
    DesiredAccess : Desired access level, refer to LsaOpenPolicy().
    PolicyHandle : Return PLSA_HANDLE.

Returns:

    ERROR_SUCCESS or  LSA error code

--*/
{
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server;

    //
    // Always initialize the object attributes to all zeroes.
    //
 
    ZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );

    if( NULL != ServerName ) 
    {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //

        InitLsaString( &ServerString, ServerName );
        Server = &ServerString;

    } 
    else 
    {
        Server = NULL;
    }

    //
    // Attempt to open the policy.
    //
    
    return( LsaOpenPolicy(
                    Server,
                    &ObjectAttributes,
                    DesiredAccess,
                    PolicyHandle ) );
}


///////////////////////////////////////////////////////////////////////////////
void
InitLsaString(
    IN OUT PLSA_UNICODE_STRING LsaString,
    IN LPWSTR String 
    )
/*++

Routine Description:

    Initialize LSA unicode string.

Parameters:

    LsaString : Pointer to LSA_UNICODE_STRING to be initialized.
    String : String to initialize LsaString.

Returns:

    None.

Note:

    Refer to LSA_UNICODE_STRING

--*/
{
    DWORD StringLength;

    if( NULL == String ) 
    {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;
        return;
    }

    StringLength = lstrlenW( String );
    LsaString->Buffer = String;
    LsaString->Length = ( USHORT ) StringLength * sizeof( WCHAR );
    LsaString->MaximumLength=( USHORT )( StringLength + 1 ) * sizeof( WCHAR );
}

//-----------------------------------------------------
BOOL 
ValidatePassword(
    IN LPWSTR pszUserName,
    IN LPWSTR pszDomain,
    IN LPWSTR pszPassword
    )
/*++

Routine Description:

    Validate user account password.

Parameters:

    pszUserName : Name of user account.
    pszDomain : Domain name.
    pszPassword : Password to be verified.

Returns:

    TRUE or FALSE.


Note:

    To debug this code, you will need to run process as service in order
    for it to verify password.  Refer to MSDN on LogonUser
    
--*/
{
    HANDLE hToken;
    BOOL bSuccess;


    //
    // To debug this code, you will need to run process as service in order
    // for it to verify password.  Refer to MSDN on LogonUser
    //

    bSuccess = LogonUser( 
                        pszUserName, 
                        pszDomain, //_TEXT("."), //pszDomain, 
                        pszPassword, 
                        LOGON32_LOGON_NETWORK_CLEARTEXT, 
                        LOGON32_PROVIDER_DEFAULT, 
                        &hToken
                    );

    if( TRUE == bSuccess )
    {
        CloseHandle( hToken );
    }
    else
    {
        DWORD dwStatus = GetLastError();

        DebugPrintf(
                _TEXT("ValidatePassword() failed with %d\n"),
                dwStatus
            );

        SetLastError(dwStatus);
    }

    return bSuccess;
}

//---------------------------------------------------------------

BOOL 
GetTextualSid(
    IN PSID pSid,            // binary Sid
    IN OUT LPTSTR TextualSid,    // buffer for Textual representation of Sid
    IN OUT LPDWORD lpdwBufferLen // required/provided TextualSid buffersize
    )
/*++

Routine Description:

    Conver a SID to string representation, code from MSDN

Parameters:

    pSid : Pointer to SID to be converted to string.
    TextualSid : On input, pointer to buffer to received converted string, on output,
                 converted SID in string form.
    lpdwBufferLen : On input, size of the buffer, on output, length of converted string
                    or required buffer size in char.

Returns:

    TRUE/FALSE, use GetLastError() to retrieve detail error code.

--*/
{
    PSID_IDENTIFIER_AUTHORITY psia;
    DWORD dwSubAuthorities;
    DWORD dwSidRev=SID_REVISION;
    DWORD dwCounter;
    DWORD dwSidSize;

    // Validate the binary SID.

    if(!IsValidSid(pSid)) 
    {
        return FALSE;
    }

    // Get the identifier authority value from the SID.

    psia = GetSidIdentifierAuthority(pSid);

    // Get the number of subauthorities in the SID.

    dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

    // Compute the buffer length.
    // S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL

    dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

    // Check input buffer length.
    // If too small, indicate the proper size and set last error.

    if (*lpdwBufferLen < dwSidSize)
    {
        *lpdwBufferLen = dwSidSize;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    // Add 'S' prefix and revision number to the string.

    dwSidSize=wsprintf(TextualSid, TEXT("S-%lu-"), dwSidRev );

    // Add SID identifier authority to the string.

    if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
    {
        dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
                    TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
                    (USHORT)psia->Value[0],
                    (USHORT)psia->Value[1],
                    (USHORT)psia->Value[2],
                    (USHORT)psia->Value[3],
                    (USHORT)psia->Value[4],
                    (USHORT)psia->Value[5]);
    }
    else
    {
        dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
                    TEXT("%lu"),
                    (ULONG)(psia->Value[5]      )   +
                    (ULONG)(psia->Value[4] <<  8)   +
                    (ULONG)(psia->Value[3] << 16)   +
                    (ULONG)(psia->Value[2] << 24)   );
    }

    // Add SID subauthorities to the string.
    //
    for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++)
    {
        dwSidSize+=wsprintf(TextualSid + dwSidSize, TEXT("-%lu"),
                    *GetSidSubAuthority(pSid, dwCounter) );
    }

    return TRUE;
}

#endif

long
GetUserTSLogonIdEx( 
    HANDLE hToken 
    )
/*++

--*/
{
    BOOL  Result;
    LONG SessionId = -1;
    ULONG ReturnLength;

#ifndef __WIN9XBUILD__
    //
    // Use the _HYDRA_ extension to GetTokenInformation to
    // return the SessionId from the token.
    //

    Result = GetTokenInformation(
                         hToken,
                         TokenSessionId,
                         &SessionId,
                         sizeof(SessionId),
                         &ReturnLength
                     );

    if( !Result ) {

        DWORD dwStatus = GetLastError();
        SessionId = -1; 

    }

#endif

    return SessionId;
}

   

long
GetUserTSLogonId()
/*++

Routine Description:

    Return client TS Session ID.

Parameters:

    None.

Returns:

    Client's TS session ID or 0 if not on TS.

Note:

    Must have impersonate user first.

--*/
{
    LONG lSessionId = -1;

#ifndef __WIN9XBUILD__
    HANDLE hToken;
    BOOL bSuccess;

    bSuccess = OpenThreadToken(
                        GetCurrentThread(),
                        TOKEN_QUERY, //TOKEN_ALL_ACCESS,
                        FALSE,
                        &hToken
                    );

    if( TRUE == bSuccess )
    {
        lSessionId = GetUserTSLogonIdEx(hToken);   
        CloseHandle(hToken);
    }

#else

    lSessionId = 0;

#endif

    return lSessionId;
}

//
//
////////////////////////////////////////////////////////////////
//
//

DWORD
RegEnumSubKeys(
    IN HKEY hKey,
    IN LPCTSTR pszSubKey,
    IN RegEnumKeyCallback pFunc,
    IN HANDLE userData
    )
/*++


--*/
{
    DWORD dwStatus;
    HKEY hSubKey = NULL;
    int index;

    LONG dwNumSubKeys;
    DWORD dwMaxSubKeyLength;
    DWORD dwSubKeyLength;
    LPTSTR pszSubKeyName = NULL;

    DWORD dwMaxValueNameLen;
    LPTSTR pszValueName = NULL;
    DWORD dwValueNameLength;

    if( NULL == hKey )
    {
        dwStatus = ERROR_INVALID_PARAMETER;
        return dwStatus;
    }

    dwStatus = RegOpenKeyEx(
                            hKey,
                            pszSubKey,
                            0,
                            KEY_ALL_ACCESS,
                            &hSubKey
                        );

    if(dwStatus != ERROR_SUCCESS)
    {
        // key does not exist
        return dwStatus;
    }

    //
    // Query number of subkeys
    //
    dwStatus = RegQueryInfoKey(
                            hSubKey,
                            NULL,
                            NULL,
                            NULL,
                            (DWORD *)&dwNumSubKeys,
                            &dwMaxSubKeyLength,
                            NULL,
                            NULL,
                            &dwMaxValueNameLen,
                            NULL,
                            NULL,
                            NULL
                        );

    if(dwStatus != ERROR_SUCCESS)
    {
        goto cleanup;
    }

    dwMaxValueNameLen++;
    pszValueName = (LPTSTR)LocalAlloc(
                                    LPTR,
                                    dwMaxValueNameLen * sizeof(TCHAR)
                                );
    if(pszValueName == NULL)
    {
        goto cleanup;
    }

    if(dwNumSubKeys > 0)
    {
        // allocate buffer for subkeys.
        dwMaxSubKeyLength++;
        pszSubKeyName = (LPTSTR)LocalAlloc(
                                            LPTR,
                                            dwMaxSubKeyLength * sizeof(TCHAR)
                                        );
        if(pszSubKeyName == NULL)
        {
            dwStatus = ERROR_OUTOFMEMORY;
            goto cleanup;
        }

        for(;dwStatus == ERROR_SUCCESS && dwNumSubKeys >= 0;)
        {
            // delete this subkey.
            dwSubKeyLength = dwMaxSubKeyLength;
            memset(pszSubKeyName, 0, dwMaxSubKeyLength * sizeof(TCHAR));

            // retrieve subkey name
            dwStatus = RegEnumKeyEx(
                                hSubKey,
                                (DWORD)--dwNumSubKeys,
                                pszSubKeyName,
                                &dwSubKeyLength,
                                NULL,
                                NULL,
                                NULL,
                                NULL
                            );

            if(dwStatus == ERROR_SUCCESS)
            {
                dwStatus = pFunc( 
                                hSubKey, 
                                pszSubKeyName, 
                                userData 
                            );
            }
        }

        if( ERROR_NO_MORE_ITEMS == dwStatus )
        {
            dwStatus = ERROR_SUCCESS;
        }
    }

cleanup:
                            
    // close the key before trying to delete it.
    if(hSubKey != NULL)
    {
        RegCloseKey(hSubKey);
    }

    if(pszValueName != NULL)
    {
        LocalFree(pszValueName);
    }

    if(pszSubKeyName != NULL)
    {
        LocalFree(pszSubKeyName);
    }

    return dwStatus;   
}    


DWORD
RegDelKey(
    IN HKEY hRegKey,
    IN LPCTSTR pszSubKey
    )
/*++

Abstract:

    Recursively delete entire registry key.

Parameter:

    hKey : Handle to a curently open key.
    pszSubKey : Pointer to NULL terminated string containing the key to be deleted.

Returns:

    Error code from RegOpenKeyEx(), RegQueryInfoKey(), 
        RegEnumKeyEx().

++*/
{
    DWORD dwStatus;
    HKEY hSubKey = NULL;
    int index;

    DWORD dwNumSubKeys;
    DWORD dwMaxSubKeyLength;
    DWORD dwSubKeyLength;
    LPTSTR pszSubKeyName = NULL;

    DWORD dwMaxValueNameLen;
    LPTSTR pszValueName = NULL;
    DWORD dwValueNameLength;

    if( NULL == hRegKey )
    {
        dwStatus = ERROR_INVALID_PARAMETER;
        return dwStatus;
    }

    dwStatus = RegOpenKeyEx(
                            hRegKey,
                            pszSubKey,
                            0,
                            KEY_ALL_ACCESS,
                            &hSubKey
                        );

    if(dwStatus != ERROR_SUCCESS)
    {
        // key does not exist
        return dwStatus;
    }

    //
    // Query number of subkeys
    //
    dwStatus = RegQueryInfoKey(
                            hSubKey,
                            NULL,
                            NULL,
                            NULL,
                            &dwNumSubKeys,
                            &dwMaxSubKeyLength,
                            NULL,
                            NULL,
                            &dwMaxValueNameLen,
                            NULL,
                            NULL,
                            NULL
                        );

    if(dwStatus != ERROR_SUCCESS)
    {
        goto cleanup;
    }

    dwMaxValueNameLen++;
    pszValueName = (LPTSTR)LocalAlloc(
                                    LPTR,
                                    dwMaxValueNameLen * sizeof(TCHAR)
                                );
    if(pszValueName == NULL)
    {
        goto cleanup;
    }

    if(dwNumSubKeys > 0)
    {
        // allocate buffer for subkeys.

        dwMaxSubKeyLength++;
        pszSubKeyName = (LPTSTR)LocalAlloc(
                                            LPTR,
                                            dwMaxSubKeyLength * sizeof(TCHAR)
                                        );
        if(pszSubKeyName == NULL)
        {
            dwStatus = ERROR_OUTOFMEMORY;
            goto cleanup;
        }


        //for(index = 0; index < dwNumSubKeys; index++)
        for(;dwStatus == ERROR_SUCCESS;)
        {
            // delete this subkey.
            dwSubKeyLength = dwMaxSubKeyLength;
            memset(pszSubKeyName, 0, dwMaxSubKeyLength * sizeof(TCHAR));

            // retrieve subkey name
            dwStatus = RegEnumKeyEx(
                                hSubKey,
                                (DWORD)0,
                                pszSubKeyName,
                                &dwSubKeyLength,
                                NULL,
                                NULL,
                                NULL,
                                NULL
                            );

            if(dwStatus == ERROR_SUCCESS)
            {
                dwStatus = RegDelKey( hSubKey, pszSubKeyName );
            }

            // ignore any error and continue on
        }
    }

cleanup:

    for(dwStatus = ERROR_SUCCESS; pszValueName != NULL && dwStatus == ERROR_SUCCESS;)
    {
        dwValueNameLength = dwMaxValueNameLen;
        memset(pszValueName, 0, dwMaxValueNameLen * sizeof(TCHAR));

        dwStatus = RegEnumValue(
                            hSubKey,
                            0,
                            pszValueName,
                            &dwValueNameLength,
                            NULL,
                            NULL,
                            NULL,
                            NULL
                        );

        if(dwStatus == ERROR_SUCCESS)
        {
            RegDeleteValue(hSubKey, pszValueName);
        }
    }   
                            
    // close the key before trying to delete it.
    if(hSubKey != NULL)
    {
        RegCloseKey(hSubKey);
    }

    // try to delete this key, will fail if any of the subkey
    // failed to delete in loop
    dwStatus = RegDeleteKey(
                            hRegKey,
                            pszSubKey
                        );



    if(pszValueName != NULL)
    {
        LocalFree(pszValueName);
    }

    if(pszSubKeyName != NULL)
    {
        LocalFree(pszSubKeyName);
    }

    return dwStatus;   
}    

//---------------------------------------------------------------
DWORD
GetUserSid(
    OUT PBYTE* ppbSid,
    OUT DWORD* pcbSid
    )
/*++

Routine Description:

    Retrieve user's SID , must impersonate client first.

Parameters:

    ppbSid : Pointer to PBYTE to receive user's SID.
    pcbSid : Pointer to DWORD to receive size of SID.

Returns:

    ERROR_SUCCESS or error code.

Note:

    Must have call ImpersonateClient(), funtion is NT specific,
    Win9X will return internal error.

--*/
{
#ifndef __WIN9XBUILD__

    BOOL bSuccess = TRUE;
    DWORD dwStatus = ERROR_SUCCESS;

    HANDLE hToken = NULL;
    DWORD dwSize = 0;
    TOKEN_USER* pToken = NULL;

    *ppbSid = NULL;
    *pcbSid = 0;

    //
    // Open current process token
    //
    bSuccess = OpenThreadToken(
                            GetCurrentThread(),
                            TOKEN_QUERY, 
                            FALSE,
                            &hToken
                        );

    if( TRUE == bSuccess )
    {
        //
        // get user's token.
        //
        GetTokenInformation(
                        hToken,
                        TokenUser,
                        NULL,
                        0,
                        &dwSize
                    );

        pToken = (TOKEN_USER *)LocalAlloc( LPTR, dwSize );
        if( NULL != pToken )
        {
            bSuccess = GetTokenInformation(
                                        hToken,
                                        TokenUser,
                                        (LPVOID) pToken,
                                        dwSize,
                                        &dwSize
                                    );

            if( TRUE == bSuccess )
            {
                //
                // GetLengthSid() return size of buffer require,
                // must call IsValidSid() first
                //
                bSuccess = IsValidSid( pToken->User.Sid );
                if( TRUE == bSuccess )
                {
                    *pcbSid = GetLengthSid( (PBYTE)pToken->User.Sid );
                    *ppbSid = (PBYTE)LocalAlloc(LPTR, *pcbSid);
                    if( NULL != *ppbSid )
                    {
                        bSuccess = CopySid(
                                            *pcbSid,
                                            *ppbSid,
                                            pToken->User.Sid
                                        );                  
                    }
                    else // fail in LocalAlloc()
                    {
                        bSuccess = FALSE;
                    }
                } // IsValidSid()
            } // GetTokenInformation()
        }
        else // LocalAlloc() fail
        {
            bSuccess = FALSE;
        }
    }

    if( TRUE != bSuccess )
    {
        dwStatus = GetLastError();

        if( NULL != *ppbSid )
        {
            LocalFree(*ppbSid);
            *ppbSid = NULL;
            *pcbSid = 0;
        }
    }

    //
    // Free resources...
    //
    if( NULL != pToken )
    {
        LocalFree(pToken);
    }

    if( NULL != hToken )
    {
        CloseHandle(hToken);
    }

    return dwStatus;

#else

    return E_UNEXPECTED;

#endif
}


//----------------------------------------------------------------
HRESULT
GetUserSidString(
    OUT CComBSTR& bstrSid
    )
/*++

Routine Description:

    Retrieve user's SID in textual form, must impersonate client first.

Parameters:

    bstrSID : Return users' SID in textual form.

Returns:

    ERROR_SUCCESS or error code.

Note:

    Must have call ImpersonateClient().

--*/
{
#ifndef __WIN9XBUILD__

    DWORD dwStatus;
    PBYTE pbSid = NULL;
    DWORD cbSid = 0;
    BOOL bSuccess = TRUE;
    LPTSTR pszTextualSid = NULL;
    DWORD dwTextualSid = 0;

    dwStatus = GetUserSid( &pbSid, &cbSid );
    if( ERROR_SUCCESS == dwStatus )
    {
        bSuccess = GetTextualSid( 
                            pbSid, 
                            NULL, 
                            &dwTextualSid 
                        );

        if( FALSE == bSuccess && ERROR_INSUFFICIENT_BUFFER == GetLastError() )
        {
            pszTextualSid = (LPTSTR)LocalAlloc(
                                            LPTR, 
                                            (dwTextualSid + 1) * sizeof(TCHAR)
                                        );

            if( NULL != pszTextualSid )
            {
                bSuccess = GetTextualSid( 
                                        pbSid, 
                                        pszTextualSid, 
                                        &dwTextualSid
                                    );

                if( TRUE == bSuccess )
                {
                    bstrSid = pszTextualSid;
                }
            }
        }

        if( FALSE == bSuccess )
        {
            dwStatus = GetLastError();
        }
    }

    if( NULL != pszTextualSid )
    {
        LocalFree(pszTextualSid);
    }

    if( NULL != pbSid )
    {
        LocalFree(pbSid);
    }

    return HRESULT_FROM_WIN32(dwStatus);

#else

    bstrSid = WIN9X_USER_SID;

    return S_OK;

#endif
}

BOOL
FileExists(
    IN  LPCTSTR           FileName,
    OUT PWIN32_FIND_DATA FindData   OPTIONAL
    )

/*++

Routine Description:

    Determine if a file exists and is accessible.
    Errormode is set (and then restored) so the user will not see
    any pop-ups.

Arguments:

    FileName - supplies full path of file to check for existance.

    FindData - if specified, receives find data for the file.

Return Value:

    TRUE if the file exists and is accessible.
    FALSE if not. GetLastError() returns extended error info.

--*/

{
    WIN32_FIND_DATA findData;
    HANDLE FindHandle;
    DWORD Error;

    FindHandle = FindFirstFile(FileName,&findData);
    if(FindHandle == INVALID_HANDLE_VALUE) 
    {
        Error = GetLastError();
    } 
    else 
    {
        FindClose(FindHandle);
        if(FindData) 
        {
            *FindData = findData;
        }
        Error = NO_ERROR;
    }

     SetLastError(Error);
    return (Error == NO_ERROR);
}


BOOL
AdjustPrivilege(
    PWSTR   Privilege
    )
/*++

Routine Description:

    This routine tries to adjust the priviliege of the current process.


Arguments:

    Privilege - String with the name of the privilege to be adjusted.

Return Value:

    Returns TRUE if the privilege could be adjusted.
    Returns FALSE, otherwise.


--*/
{
    HANDLE              TokenHandle;
    LUID_AND_ATTRIBUTES LuidAndAttributes;

    TOKEN_PRIVILEGES    TokenPrivileges;


    if( !OpenProcessToken( GetCurrentProcess(),
                           TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                           &TokenHandle ) ) {
        return( FALSE );
    }


    if( !LookupPrivilegeValue( NULL,
                               Privilege, // (LPWSTR)SE_SECURITY_NAME,
                               &( LuidAndAttributes.Luid ) ) ) {
        return( FALSE );
    }

    LuidAndAttributes.Attributes = SE_PRIVILEGE_ENABLED;
    TokenPrivileges.PrivilegeCount = 1;
    TokenPrivileges.Privileges[0] = LuidAndAttributes;

    if( !AdjustTokenPrivileges( TokenHandle,
                                FALSE,
                                &TokenPrivileges,
                                0,
                                NULL,
                                NULL ) ) {
        return( FALSE );
    }

    if( GetLastError() != NO_ERROR ) {
        return( FALSE );
    }
    return( TRUE );
}