/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    checker.cxx

Abstract:

    IIS Services IISADMIN Extension
    Unicode Metadata Sink.

Author:

    Michael W. Thomas            11-19-98

--*/
#include <cominc.hxx>
#include <lmaccess.h>
#include <lmserver.h>
#include <lmapibuf.h>
#include <lmerr.h>
#include <ntlsa.h>
#include <time.h>

#include <ntsam.h>
#include <netlib.h>
#include <resource.h>

#define SECURITY_WIN32
#define ISSP_LEVEL  32
#define ISSP_MODE   1
#include <sspi.h>
#include <sspi.h>
#include <eventlog.hxx>
#include "extend.h"

#include <comadmin.h>
#include <sddl.h>

#include "wmrgexp.h"

typedef TCHAR USERNAME_STRING_TYPE[MAX_PATH];
typedef TCHAR PASSWORD_STRING_TYPE[LM20_PWLEN+1];

const LPCWSTR ROOTMDPath = L"/LM/W3SVC";

#define IIS_WP_GROUP        L"IIS_WPG"

typedef enum {
    GUFM_SUCCESS,
    GUFM_NO_PATH,
    GUFM_NO_PASSWORD,
    GUFM_NO_USER_ID
} GUFM_RETURN;

BOOL  ValidatePassword(IN LPCTSTR UserName,IN LPCTSTR Domain,IN LPCTSTR Password);
void  InitLsaString(PLSA_UNICODE_STRING LsaString,LPWSTR String);
DWORD GetPrincipalSID (LPCTSTR Principal,PSID *Sid,BOOL *pbWellKnownSID);
DWORD OpenPolicy(LPTSTR ServerName,DWORD DesiredAccess,PLSA_HANDLE PolicyHandle);
DWORD AddRightToUserAccount(LPCTSTR szAccountName, LPTSTR PrivilegeName);
DWORD DoesUserHaveThisRight(LPCTSTR szAccountName, LPTSTR PrivilegeName,BOOL *fHaveThatRight);
HRESULT UpdateComApplications(IMDCOM * pcCom,LPCTSTR szWamUserName,LPCTSTR szWamUserPass);
int   IsDomainController(void);
BOOL  WaitForDCAvailability(void);
HRESULT UpdateAdminAcl(IMDCOM *pcCom, LPCWSTR szPath, LPCWSTR szAccountName);


// these two lines for logging event about account recreation
EVENT_LOG *g_eventLogForAccountRecreation = NULL; 
BOOL CreateEventLogObject();



VOID  UpdateUserRights (LPCTSTR  account,LPTSTR pstrRights[],DWORD dwNofRights)
{
    DWORD status;
    BOOL  fPresence;

    
    for (DWORD i=0;i<dwNofRights;i++)
    {
        
        
        status = DoesUserHaveThisRight(account,pstrRights[i],&fPresence);
        if (!NT_SUCCESS(status))
        {
            DBGPRINTF(( DBG_CONTEXT,"[UpdateAnonymousUser] DoesUserHaveThisRight returned err=0x%0X for account %s right %s\n",status,account,pstrRights[i]));
        }
        else
        {
            if (!fPresence)
            {
                status = AddRightToUserAccount(account,pstrRights[i]);
                if (!NT_SUCCESS(status))
                {
                    DBGPRINTF(( DBG_CONTEXT,"[UpdateAnonymousUser] AddRightToUserAccount returned err=0x%0X for account %s right %s\n",status,account,pstrRights[i]));
                }
            }
        }
    }
}

DWORD AddRightToUserAccount(LPCTSTR szAccountName, LPTSTR PrivilegeName)
{
    BOOL fEnabled = FALSE;
    NTSTATUS status;
	LSA_UNICODE_STRING UserRightString;
    LSA_HANDLE PolicyHandle = NULL;

    // Create a LSA_UNICODE_STRING for the privilege name.
    InitLsaString(&UserRightString, PrivilegeName);

    // get the sid of szAccountName
    PSID pSID = NULL;
    BOOL bWellKnownSID = FALSE;

    status = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);

    if (status != ERROR_SUCCESS) 
    {
        DBGPRINTF(( DBG_CONTEXT,"[AddRightToUserAccount] GetPrincipalSID returned err=0x%0X\n",status));
        return (status);
    }


    status = OpenPolicy(NULL, POLICY_ALL_ACCESS,&PolicyHandle);
    if ( status == NERR_Success )
    {
		UINT i;
        LSA_UNICODE_STRING *rgUserRights = NULL;
		ULONG cRights;
	
		status = LsaAddAccountRights (
				 PolicyHandle,
				 pSID,
				 &UserRightString,
				 1);
    }

    if (PolicyHandle)
    {
        LsaClose(PolicyHandle);
    }
    if (pSID) 
    {
        if (bWellKnownSID)
        {
            FreeSid (pSID);
        }
        else
        {
            free (pSID);
        }
    }
    return status;
}



DWORD DoesUserHaveThisRight(LPCTSTR szAccountName, LPTSTR PrivilegeName,BOOL *fHaveThatRight)
{
    BOOL fEnabled = FALSE;
    NTSTATUS status;
	LSA_UNICODE_STRING UserRightString;
    LSA_HANDLE PolicyHandle = NULL;


    *fHaveThatRight = FALSE;

    // Create a LSA_UNICODE_STRING for the privilege name.
    InitLsaString(&UserRightString, PrivilegeName);

    // get the sid of szAccountName
    PSID pSID = NULL;
    BOOL bWellKnownSID = FALSE;

    status = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);

    if (status != ERROR_SUCCESS) 
    {
        return status;
    }


    status = OpenPolicy(NULL, POLICY_ALL_ACCESS,&PolicyHandle);
    if ( status == NERR_Success )
    {
		UINT i;
        LSA_UNICODE_STRING *rgUserRights = NULL;
		ULONG cRights;
	
		status = LsaEnumerateAccountRights(
				 PolicyHandle,
				 pSID,
				 &rgUserRights,
				 &cRights);

		if (status==STATUS_OBJECT_NAME_NOT_FOUND)
        {
			// no rights/privileges for this account
            status = ERROR_SUCCESS;
			fEnabled = FALSE;
		}
		else if (!NT_SUCCESS(status)) 
        {
            //iisDebugOut((LOG_TYPE_ERROR, _T("DoesUserHaveBasicRights:GetPrincipalSID:Failed to enumerate rights: status 0x%08lx\n"), status));
			goto DoesUserHaveBasicRights_Exit;
		}

		for(i=0; i < cRights; i++) 
        {
            if ( RtlEqualUnicodeString(&rgUserRights[i],&UserRightString,FALSE) ) 
            {
                fEnabled = TRUE;
                break;
            }
		}
		
        if (rgUserRights) 
        {
            LsaFreeMemory(rgUserRights);
        }
    }

DoesUserHaveBasicRights_Exit:

    if (PolicyHandle)
    {
        LsaClose(PolicyHandle);
    }
    if (pSID) 
    {
        if (bWellKnownSID)
        {
            FreeSid (pSID);
        }
        else
        {
            free (pSID);
        }
    }

    *fHaveThatRight = fEnabled;
    return status;
}



DWORD
GetCurrentUserSID (
    PSID *Sid
    )
{
    DWORD dwReturn = ERROR_SUCCESS;
    TOKEN_USER  *tokenUser = NULL;
    HANDLE      tokenHandle = NULL;
    DWORD       tokenSize;
    DWORD       sidLength;

    if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &tokenHandle))
    {
        GetTokenInformation (tokenHandle, TokenUser, tokenUser, 0, &tokenSize);

        tokenUser = (TOKEN_USER *) malloc (tokenSize);
        if (!tokenUser)
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return GetLastError();
        }

        if (GetTokenInformation (tokenHandle, TokenUser, tokenUser, tokenSize, &tokenSize))
        {
            sidLength = GetLengthSid (tokenUser->User.Sid);
            *Sid = (PSID) malloc (sidLength);
            if (*Sid)
            {
                memcpy (*Sid, tokenUser->User.Sid, sidLength);
            }
            CloseHandle (tokenHandle);
        } else
            dwReturn = GetLastError();

        if (tokenUser)
            free(tokenUser);

    } else
        dwReturn = GetLastError();

    return dwReturn;
}

DWORD
CreateNewSD (
    SECURITY_DESCRIPTOR **SD
    )
{
    PACL    dacl;
    DWORD   sidLength;
    PSID    sid = NULL;
    PSID    groupSID;
    PSID    ownerSID;
    DWORD   returnValue;

    *SD = NULL;

    returnValue = GetCurrentUserSID (&sid);
    if (returnValue != ERROR_SUCCESS) {
        if (sid)
            free(sid);
        return returnValue;
    }

    sidLength = GetLengthSid (sid);

    *SD = (SECURITY_DESCRIPTOR *) malloc (
        (sizeof (ACL)+sizeof (ACCESS_ALLOWED_ACE)+sidLength) +
        (2 * sidLength) +
        sizeof (SECURITY_DESCRIPTOR));
    if (!*SD)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return GetLastError();
    }

    groupSID = (SID *) (*SD + 1);
    ownerSID = (SID *) (((BYTE *) groupSID) + sidLength);
    dacl = (ACL *) (((BYTE *) ownerSID) + sidLength);

    if (!InitializeSecurityDescriptor (*SD, SECURITY_DESCRIPTOR_REVISION))
    {
        free (*SD);
        free (sid);
        return GetLastError();
    }

    if (!InitializeAcl (dacl,
                        sizeof (ACL)+sizeof (ACCESS_ALLOWED_ACE)+sidLength,
                        ACL_REVISION2))
    {
        free (*SD);
        free (sid);
        return GetLastError();
    }

    if (!AddAccessAllowedAce (dacl,
                              ACL_REVISION2,
                              COM_RIGHTS_EXECUTE,
                              sid))
    {
        free (*SD);
        free (sid);
        return GetLastError();
    }

    if (!SetSecurityDescriptorDacl (*SD, TRUE, dacl, FALSE))
    {
        free (*SD);
        free (sid);
        return GetLastError();
    }

    memcpy (groupSID, sid, sidLength);
    if (!SetSecurityDescriptorGroup (*SD, groupSID, FALSE))
    {
        free (*SD);
        free (sid);
        return GetLastError();
    }

    memcpy (ownerSID, sid, sidLength);
    if (!SetSecurityDescriptorOwner (*SD, ownerSID, FALSE))
    {
        free (*SD);
        free (sid);
        return GetLastError();
    }

    if (sid)
        free(sid);
    return ERROR_SUCCESS;
}

DWORD
GetNamedValueSD (
    HKEY RootKey,
    LPTSTR KeyName,
    LPTSTR ValueName,
    SECURITY_DESCRIPTOR **SD,
    BOOL *NewSD
    )
{
    DWORD               returnValue;
    HKEY                registryKey;
    DWORD               valueType;
    DWORD               valueSize = 0;

    *NewSD = FALSE;

    //
    // Get the security descriptor from the named value. If it doesn't
    // exist, create a fresh one.
    //

    returnValue = RegOpenKeyEx (RootKey, KeyName, 0, KEY_ALL_ACCESS, &registryKey);

    if (returnValue != ERROR_SUCCESS)
    {
        if (returnValue == ERROR_FILE_NOT_FOUND)
        {
            *SD = NULL;
            returnValue = CreateNewSD (SD);
            if (returnValue != ERROR_SUCCESS) {
                if (*SD)
                    free(*SD);
                return returnValue;
            }

            *NewSD = TRUE;
            return ERROR_SUCCESS;
        } else
            return returnValue;
    }

    returnValue = RegQueryValueEx (registryKey, ValueName, NULL, &valueType, NULL, &valueSize);

    if (returnValue && returnValue != ERROR_INSUFFICIENT_BUFFER)
    {
        *SD = NULL;
        returnValue = CreateNewSD (SD);
        if (returnValue != ERROR_SUCCESS) {
            if (*SD)
                free(*SD);
            return returnValue;
        }

        *NewSD = TRUE;
    } else
    {
        *SD = (SECURITY_DESCRIPTOR *) malloc (valueSize);
        if (!*SD)
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return GetLastError();
        }

        returnValue = RegQueryValueEx (registryKey,
                                       ValueName,
                                       NULL,
                                       &valueType,
                                       (LPBYTE) *SD,
                                       &valueSize);
        if (returnValue)
        {
            if (*SD)
                free (*SD);

            *SD = NULL;
            returnValue = CreateNewSD (SD);
            if (returnValue != ERROR_SUCCESS) {
                if (*SD)
                    free(*SD);
                return returnValue;
            }

            *NewSD = TRUE;
        }
    }

    RegCloseKey (registryKey);

    return ERROR_SUCCESS;
}

DWORD
GetPrincipalSID (
    LPCTSTR Principal,
    PSID *Sid,
    BOOL *pbWellKnownSID
    )
{

    DWORD returnValue=ERROR_SUCCESS;
    SID_IDENTIFIER_AUTHORITY SidIdentifierNTAuthority = SECURITY_NT_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY SidIdentifierWORLDAuthority = SECURITY_WORLD_SID_AUTHORITY;
    PSID_IDENTIFIER_AUTHORITY pSidIdentifierAuthority;
    BYTE Count;
    DWORD dwRID[8];
    TCHAR pszPrincipal[MAX_PATH];

    *pbWellKnownSID = TRUE;
    memset(&(dwRID[0]), 0, 8 * sizeof(DWORD));

    DBG_ASSERT(wcslen(Principal) < MAX_PATH);
    wcscpy(pszPrincipal, Principal);
    _wcslwr(pszPrincipal);
    if ( wcsstr(pszPrincipal, TEXT("administrators")) != NULL ) {
        // Administrators group
        pSidIdentifierAuthority = &SidIdentifierNTAuthority;
        Count = 2;
        dwRID[0] = SECURITY_BUILTIN_DOMAIN_RID;
        dwRID[1] = DOMAIN_ALIAS_RID_ADMINS;
    } else if ( wcsstr(pszPrincipal, TEXT("system")) != NULL) {
        // SYSTEM
        pSidIdentifierAuthority = &SidIdentifierNTAuthority;
        Count = 1;
        dwRID[0] = SECURITY_LOCAL_SYSTEM_RID;
    } else if ( wcsstr(pszPrincipal, TEXT("interactive")) != NULL) {
        // INTERACTIVE
        pSidIdentifierAuthority = &SidIdentifierNTAuthority;
        Count = 1;
        dwRID[0] = SECURITY_INTERACTIVE_RID;
    } else if ( wcsstr(pszPrincipal, TEXT("everyone")) != NULL) {
        // Everyone
        pSidIdentifierAuthority = &SidIdentifierWORLDAuthority;
        Count = 1;
        dwRID[0] = SECURITY_WORLD_RID;
    } else {
        *pbWellKnownSID = FALSE;
    }

    if (*pbWellKnownSID) {
        if ( !AllocateAndInitializeSid(pSidIdentifierAuthority,
                                    (BYTE)Count,
                                            dwRID[0],
                                            dwRID[1],
                                            dwRID[2],
                                            dwRID[3],
                                            dwRID[4],
                                            dwRID[5],
                                            dwRID[6],
                                            dwRID[7],
                                    Sid) ) {
            returnValue = GetLastError();
        }
    } else {
        // get regular account sid
        DWORD        sidSize;
        TCHAR        refDomain [256];
        DWORD        refDomainSize;
        SID_NAME_USE snu;

        sidSize = 0;
        refDomainSize = 255;

        LookupAccountName (NULL,
                           pszPrincipal,
                           *Sid,
                           &sidSize,
                           refDomain,
                           &refDomainSize,
                           &snu);

        returnValue = GetLastError();

        if (returnValue == ERROR_INSUFFICIENT_BUFFER) {
            *Sid = (PSID) malloc (sidSize);
            if (!*Sid)
            {
                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                return GetLastError();
            }
            refDomainSize = 255;

            if (!LookupAccountName (NULL,
                                    pszPrincipal,
                                    *Sid,
                                    &sidSize,
                                    refDomain,
                                    &refDomainSize,
                                    &snu))
            {
                returnValue = GetLastError();
            } else {
                returnValue = ERROR_SUCCESS;
            }
        }
    }

    return returnValue;
}

DWORD
CopyACL (
    PACL OldACL,
    PACL NewACL
    )
{
    ACL_SIZE_INFORMATION  aclSizeInfo;
    LPVOID                ace;
    ACE_HEADER            *aceHeader;
    ULONG                 i;

    GetAclInformation (OldACL,
                       (LPVOID) &aclSizeInfo,
                       (DWORD) sizeof (aclSizeInfo),
                       AclSizeInformation);

    //
    // Copy all of the ACEs to the new ACL
    //

    for (i = 0; i < aclSizeInfo.AceCount; i++)
    {
        //
        // Get the ACE and header info
        //

        if (!GetAce (OldACL, i, &ace))
            return GetLastError();

        aceHeader = (ACE_HEADER *) ace;

        //
        // Add the ACE to the new list
        //

        if (!AddAce (NewACL, ACL_REVISION, 0xffffffff, ace, aceHeader->AceSize))
            return GetLastError();
    }

    return ERROR_SUCCESS;
}


DWORD
AddAccessAllowedACEToACL (
    PACL *Acl,
    DWORD PermissionMask,
    LPTSTR Principal
    )
{
    ACL_SIZE_INFORMATION  aclSizeInfo;
    int                   aclSize;
    DWORD                 returnValue = ERROR_SUCCESS;
    PSID                  principalSID = NULL;
    PACL                  oldACL, newACL;
    BOOL                  bWellKnownSID = FALSE;

    oldACL = *Acl;

    returnValue = GetPrincipalSID (Principal, &principalSID, &bWellKnownSID);
    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    GetAclInformation (oldACL,
                       (LPVOID) &aclSizeInfo,
                       (DWORD) sizeof (ACL_SIZE_INFORMATION),
                       AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse +
              sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) +
              GetLengthSid (principalSID) - sizeof (DWORD);

    newACL = (PACL) new BYTE [aclSize];

    if (!InitializeAcl (newACL, aclSize, ACL_REVISION))
    {
        returnValue = GetLastError();
        goto cleanup;
    }

    returnValue = CopyACL (oldACL, newACL);
    if (returnValue != ERROR_SUCCESS)
    {
        goto cleanup;
    }

    if (!AddAccessAllowedAce (newACL, ACL_REVISION2, PermissionMask, principalSID))
    {
        returnValue = GetLastError();
        goto cleanup;
    }

    *Acl = newACL;
	newACL = NULL;

cleanup:

	// BugFix: 57654 Whistler
	//         Prefix bug leaking memory in error condition.
	//         By setting the newACL to NULL above if we have
	//         relinquished the memory to *Acl, we avoid releasing
	//         memory we have passed back to the caller.
	//         EBK 5/5/2000		
	if (newACL)
	{
		delete [] newACL;
		newACL = NULL;
	}

    if (principalSID) {
        if (bWellKnownSID)
            FreeSid (principalSID);
        else
            free (principalSID);
    }

    return returnValue;
}

DWORD
RemovePrincipalFromACL (
    PACL Acl,
    LPTSTR Principal
    )
{
    ACL_SIZE_INFORMATION    aclSizeInfo;
    ULONG                   i;
    LPVOID                  ace;
    ACCESS_ALLOWED_ACE      *accessAllowedAce;
    ACCESS_DENIED_ACE       *accessDeniedAce;
    SYSTEM_AUDIT_ACE        *systemAuditAce;
    PSID                    principalSID = NULL;
    DWORD                   returnValue = ERROR_SUCCESS;
    ACE_HEADER              *aceHeader;
    BOOL                    bWellKnownSID = FALSE;

    returnValue = GetPrincipalSID (Principal, &principalSID, &bWellKnownSID);
    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    GetAclInformation (Acl,
                       (LPVOID) &aclSizeInfo,
                       (DWORD) sizeof (ACL_SIZE_INFORMATION),
                       AclSizeInformation);

    for (i = 0; i < aclSizeInfo.AceCount; i++)
    {
        if (!GetAce (Acl, i, &ace))
        {
            returnValue = GetLastError();
            break;
        }

        aceHeader = (ACE_HEADER *) ace;

        if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
        {
            accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;

            if (EqualSid (principalSID, (PSID) &accessAllowedAce->SidStart))
            {
                DeleteAce (Acl, i);
                break;
            }
        } else

        if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
        {
            accessDeniedAce = (ACCESS_DENIED_ACE *) ace;

            if (EqualSid (principalSID, (PSID) &accessDeniedAce->SidStart))
            {
                DeleteAce (Acl, i);
                break;
            }
        } else

        if (aceHeader->AceType == SYSTEM_AUDIT_ACE_TYPE)
        {
            systemAuditAce = (SYSTEM_AUDIT_ACE *) ace;

            if (EqualSid (principalSID, (PSID) &systemAuditAce->SidStart))
            {
                DeleteAce (Acl, i);
                break;
            }
        }
    }

    if (principalSID) {
        if (bWellKnownSID)
            FreeSid (principalSID);
        else
            free (principalSID);
    }

    return returnValue;
}

DWORD
MakeSDAbsolute (
    PSECURITY_DESCRIPTOR OldSD,
    PSECURITY_DESCRIPTOR *NewSD
    )
{
    PSECURITY_DESCRIPTOR  sd = NULL;
    DWORD                 descriptorSize;
    DWORD                 daclSize;
    DWORD                 saclSize;
    DWORD                 ownerSIDSize;
    DWORD                 groupSIDSize;
    PACL                  dacl = NULL;
    PACL                  sacl = NULL;
    PSID                  ownerSID = NULL;
    PSID                  groupSID = NULL;
    BOOL                  present;
    BOOL                  systemDefault;

    //
    // Get SACL
    //

    if (!GetSecurityDescriptorSacl (OldSD, &present, &sacl, &systemDefault))
        return GetLastError();

    if (sacl && present)
    {
        saclSize = sacl->AclSize;
    } else saclSize = 0;

    //
    // Get DACL
    //

    if (!GetSecurityDescriptorDacl (OldSD, &present, &dacl, &systemDefault))
        return GetLastError();

    if (dacl && present)
    {
        daclSize = dacl->AclSize;
    } else daclSize = 0;

    //
    // Get Owner
    //

    if (!GetSecurityDescriptorOwner (OldSD, &ownerSID, &systemDefault))
        return GetLastError();

    ownerSIDSize = GetLengthSid (ownerSID);

    //
    // Get Group
    //

    if (!GetSecurityDescriptorGroup (OldSD, &groupSID, &systemDefault))
        return GetLastError();

    groupSIDSize = GetLengthSid (groupSID);

    //
    // Do the conversion
    //

    descriptorSize = 0;

    MakeAbsoluteSD (OldSD, sd, &descriptorSize, dacl, &daclSize, sacl,
                    &saclSize, ownerSID, &ownerSIDSize, groupSID,
                    &groupSIDSize);

    sd = (PSECURITY_DESCRIPTOR) new BYTE [SECURITY_DESCRIPTOR_MIN_LENGTH];
    if (!sd)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return GetLastError();
    }
    if (!InitializeSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION))
        return GetLastError();

    if (!MakeAbsoluteSD (OldSD, sd, &descriptorSize, dacl, &daclSize, sacl,
                         &saclSize, ownerSID, &ownerSIDSize, groupSID,
                         &groupSIDSize))
        return GetLastError();

    *NewSD = sd;
    return ERROR_SUCCESS;
}

DWORD
SetNamedValueSD (
    HKEY RootKey,
    LPTSTR KeyName,
    LPTSTR ValueName,
    SECURITY_DESCRIPTOR *SD
    )
{
    DWORD   returnValue;
    DWORD   disposition;
    HKEY    registryKey;

    //
    // Create new key or open existing key
    //

    returnValue = RegCreateKeyEx (RootKey,
                                  KeyName,
                                  0,
                                  TEXT(""),
                                  0,
                                  KEY_ALL_ACCESS,
                                  NULL,
                                  &registryKey,
                                  &disposition);
    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    //
    // Write the security descriptor
    //

    returnValue = RegSetValueEx (registryKey,
                                 ValueName,
                                 0,
                                 REG_BINARY,
                                 (LPBYTE) SD,
                                 GetSecurityDescriptorLength (SD));
    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    RegCloseKey (registryKey);

    return ERROR_SUCCESS;
}


DWORD
RemovePrincipalFromNamedValueSD (
    HKEY RootKey,
    LPTSTR KeyName,
    LPTSTR ValueName,
    LPTSTR Principal
    )
{
    DWORD               returnValue = ERROR_SUCCESS;
    SECURITY_DESCRIPTOR *sd = NULL;
    SECURITY_DESCRIPTOR *sdSelfRelative = NULL;
    SECURITY_DESCRIPTOR *sdAbsolute = NULL;
    DWORD               secDescSize;
    BOOL                present;
    BOOL                defaultDACL;
    PACL                dacl = NULL;
    BOOL                newSD = FALSE;
    BOOL                fFreeAbsolute = TRUE;

    returnValue = GetNamedValueSD (RootKey, KeyName, ValueName, &sd, &newSD);

    //
    // Get security descriptor from registry or create a new one
    //

    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    if (!GetSecurityDescriptorDacl (sd, &present, &dacl, &defaultDACL)) {
        returnValue = GetLastError();
        goto Cleanup;
    }

    //
    // If the security descriptor is new, add the required Principals to it
    //

    if (newSD)
    {
        AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("SYSTEM"));
        AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("INTERACTIVE"));
    }

    //
    // Remove the Principal that the caller wants removed
    //

    returnValue = RemovePrincipalFromACL (dacl, Principal);
    if (returnValue != ERROR_SUCCESS)
        goto Cleanup;

    //
    // Make the security descriptor absolute if it isn't new
    //

    if (!newSD) {
        MakeSDAbsolute ((PSECURITY_DESCRIPTOR) sd, (PSECURITY_DESCRIPTOR *) &sdAbsolute);
        fFreeAbsolute = TRUE;
    } else {
        sdAbsolute = sd;
        fFreeAbsolute = FALSE;
    }

    //
    // Set the discretionary ACL on the security descriptor
    //

    if (!SetSecurityDescriptorDacl (sdAbsolute, TRUE, dacl, FALSE)) {
        returnValue = GetLastError();
        goto Cleanup;
    }

    //
    // Make the security descriptor self-relative so that we can
    // store it in the registry
    //

    secDescSize = 0;
    MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize);
    sdSelfRelative = (SECURITY_DESCRIPTOR *) malloc (secDescSize);
    if (!sdSelfRelative)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        returnValue = GetLastError();
        goto Cleanup;
    }

    if (!MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize)) {
        returnValue = GetLastError();
        goto Cleanup;
    }

    //
    // Store the security descriptor in the registry
    //

    SetNamedValueSD (RootKey, KeyName, ValueName, sdSelfRelative);

Cleanup:
    if (sd)
        free (sd);
    if (sdSelfRelative)
        free (sdSelfRelative);
    if (fFreeAbsolute && sdAbsolute)
        free (sdAbsolute);

    return returnValue;
}

DWORD
AddAccessDeniedACEToACL (
    PACL *Acl,
    DWORD PermissionMask,
    LPTSTR Principal
    )
{
    ACL_SIZE_INFORMATION  aclSizeInfo;
    int                   aclSize;
    DWORD                 returnValue = ERROR_SUCCESS;
    PSID                  principalSID = NULL;
    PACL                  oldACL, newACL;
    BOOL                  bWellKnownSID = FALSE;
    oldACL = *Acl;

    returnValue = GetPrincipalSID (Principal, &principalSID, &bWellKnownSID);
    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    GetAclInformation (oldACL,
                       (LPVOID) &aclSizeInfo,
                       (DWORD) sizeof (ACL_SIZE_INFORMATION),
                       AclSizeInformation);

    aclSize = aclSizeInfo.AclBytesInUse +
              sizeof (ACL) + sizeof (ACCESS_DENIED_ACE) +
              GetLengthSid (principalSID) - sizeof (DWORD);

    newACL = (PACL) new BYTE [aclSize];

    if (!InitializeAcl (newACL, aclSize, ACL_REVISION))
    {
        returnValue = GetLastError();
        goto cleanup;
    }

    if (!AddAccessDeniedAce (newACL, ACL_REVISION2, PermissionMask, principalSID))
    {
        returnValue = GetLastError();
        goto cleanup;
    }

    returnValue = CopyACL (oldACL, newACL);
    if (returnValue != ERROR_SUCCESS)
    {
        goto cleanup;
    }

    *Acl = newACL;
	newACL = NULL;

cleanup:

	// BugFix: 57654 Whistler
	//         Prefix bug leaking memory in error condition.
	//         By setting the newACL to NULL above if we have
	//         relinquished the memory to *Acl, we avoid releasing
	//         memory we have passed back to the caller.
	//         EBK 5/5/2000		
	if (newACL)
	{
		delete[] newACL;
		newACL = NULL;
	}

    if (principalSID) {
        if (bWellKnownSID)
            FreeSid (principalSID);
        else
            free (principalSID);
    }

    return returnValue;
}

DWORD
AddPrincipalToNamedValueSD (
    HKEY RootKey,
    LPTSTR KeyName,
    LPTSTR ValueName,
    LPTSTR Principal,
    BOOL Permit
    )
{
    DWORD               returnValue = ERROR_SUCCESS;
    SECURITY_DESCRIPTOR *sd = NULL;
    SECURITY_DESCRIPTOR *sdSelfRelative = NULL;
    SECURITY_DESCRIPTOR *sdAbsolute = NULL;
    DWORD               secDescSize;
    BOOL                present;
    BOOL                defaultDACL;
    PACL                dacl;
    BOOL                newSD = FALSE;
    BOOL                fFreeAbsolute = TRUE;


    returnValue = GetNamedValueSD (RootKey, KeyName, ValueName, &sd, &newSD);

    //
    // Get security descriptor from registry or create a new one
    //

    if (returnValue != ERROR_SUCCESS)
        return returnValue;

    if (!GetSecurityDescriptorDacl (sd, &present, &dacl, &defaultDACL)) {
        returnValue = GetLastError();
        goto Cleanup;
    }

    if (newSD)
    {
        AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("SYSTEM"));
        AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("INTERACTIVE"));
    }

    //
    // Add the Principal that the caller wants added
    //

    if (Permit)
        returnValue = AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, Principal);
    else
        returnValue = AddAccessDeniedACEToACL (&dacl, GENERIC_ALL, Principal);

    if (returnValue != ERROR_SUCCESS)
        goto Cleanup;

    //
    // Make the security descriptor absolute if it isn't new
    //

    if (!newSD) {
        MakeSDAbsolute ((PSECURITY_DESCRIPTOR) sd, (PSECURITY_DESCRIPTOR *) &sdAbsolute);
        fFreeAbsolute = TRUE;
    } else {
        sdAbsolute = sd;
        fFreeAbsolute = FALSE;
    }

    //
    // Set the discretionary ACL on the security descriptor
    //

    if (!SetSecurityDescriptorDacl (sdAbsolute, TRUE, dacl, FALSE)) {
        returnValue = GetLastError();
        goto Cleanup;
    }

    //
    // Make the security descriptor self-relative so that we can
    // store it in the registry
    //

    secDescSize = 0;
    MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize);
    sdSelfRelative = (SECURITY_DESCRIPTOR *) malloc (secDescSize);
    if (!sdSelfRelative)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        returnValue = GetLastError();
        goto Cleanup;
    }


    if (!MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize)) {
        returnValue = GetLastError();
        goto Cleanup;
    }

    //
    // Store the security descriptor in the registry
    //

    SetNamedValueSD (RootKey, KeyName, ValueName, sdSelfRelative);

Cleanup:
    if (sd)
        free (sd);
    if (sdSelfRelative)
        free (sdSelfRelative);
    if (fFreeAbsolute && sdAbsolute)
        free (sdAbsolute);

    return returnValue;
}


DWORD
ChangeDCOMAccessACL (
    LPTSTR Principal,
    BOOL SetPrincipal,
    BOOL Permit
    )
{

    DWORD   err;

    if (SetPrincipal)
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
                                               TEXT("Software\\Microsoft\\OLE"),
                                               TEXT("DefaultAccessPermission"),
                                               Principal);
        err = AddPrincipalToNamedValueSD (HKEY_LOCAL_MACHINE,
                                          TEXT("Software\\Microsoft\\OLE"),
                                          TEXT("DefaultAccessPermission"),
                                          Principal,
                                          Permit);
    }
    else
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
                                               TEXT("Software\\Microsoft\\OLE"),
                                               TEXT("DefaultAccessPermission"),
                                               Principal);
    }
    return err;
}

DWORD
ChangeDCOMLaunchACL (
    LPTSTR Principal,
    BOOL SetPrincipal,
    BOOL Permit
    )
{

    TCHAR   keyName [256] = TEXT("Software\\Microsoft\\OLE");
    DWORD   err;

    if (SetPrincipal)
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
                                               keyName,
                                               TEXT("DefaultLaunchPermission"),
                                               Principal);
        err = AddPrincipalToNamedValueSD (HKEY_LOCAL_MACHINE,
                                          keyName,
                                          TEXT("DefaultLaunchPermission"),
                                          Principal,
                                          Permit);
    }
    else
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
                                               keyName,
                                               TEXT("DefaultLaunchPermission"),
                                               Principal);
    }
    return err;
}



GUFM_RETURN GetUserFromMetabase(IMDCOM *pcCom,
                                LPWSTR pszPath,
                                DWORD dwUserMetaId,
                                DWORD dwPasswordMetaId,
                                USERNAME_STRING_TYPE ustUserBuf,
                                PASSWORD_STRING_TYPE pstPasswordBuf)
{

    HRESULT hresTemp;
    GUFM_RETURN  gufmReturn = GUFM_SUCCESS;
    METADATA_RECORD mdrData;
    DWORD dwRequiredDataLen;

    METADATA_HANDLE mhOpenHandle;


    hresTemp = pcCom->ComMDOpenMetaObject(METADATA_MASTER_ROOT_HANDLE,
                                            pszPath,
                                            METADATA_PERMISSION_READ,
                                            OPEN_TIMEOUT_VALUE,
                                            &mhOpenHandle);
    if (FAILED(hresTemp)) {
        gufmReturn = GUFM_NO_PATH;
    }
    else {
        MD_SET_DATA_RECORD_EXT(&mdrData,
                               dwUserMetaId,
                               METADATA_NO_ATTRIBUTES,
                               ALL_METADATA,
                               STRING_METADATA,
                               MAX_PATH * sizeof(TCHAR),
                               (PBYTE)ustUserBuf)

        hresTemp = pcCom->ComMDGetMetaData(mhOpenHandle,
                                           NULL,
                                           &mdrData,
                                           &dwRequiredDataLen);

        if (FAILED(hresTemp) || (ustUserBuf[0] == (TCHAR)'\0')) {
            gufmReturn = GUFM_NO_USER_ID;
        }
        else {

            MD_SET_DATA_RECORD_EXT(&mdrData,
                                   dwPasswordMetaId,
                                   METADATA_NO_ATTRIBUTES,
                                   ALL_METADATA,
                                   STRING_METADATA,
                                   MAX_PATH * sizeof(TCHAR),
                                   (PBYTE)pstPasswordBuf)

            hresTemp = pcCom->ComMDGetMetaData(mhOpenHandle,
                                               NULL,
                                               &mdrData,
                                               &dwRequiredDataLen);
            if (FAILED(hresTemp)) {
                gufmReturn = GUFM_NO_PASSWORD;
            }
        }
        pcCom->ComMDCloseMetaObject(mhOpenHandle);
    }

    return gufmReturn;
}

BOOL WritePasswordToMetabase(IMDCOM *pcCom,
                             LPWSTR pszPath,
                             DWORD dwPasswordMetaId,
                             PASSWORD_STRING_TYPE pstPasswordBuf)
{

    HRESULT hresReturn;
    BOOL fReturn = FALSE;
    METADATA_RECORD mdrData;

    METADATA_HANDLE mhOpenHandle;


    hresReturn = pcCom->ComMDOpenMetaObject(METADATA_MASTER_ROOT_HANDLE,
                                            pszPath,
                                            METADATA_PERMISSION_WRITE,
                                            OPEN_TIMEOUT_VALUE,
                                            &mhOpenHandle);
    if (SUCCEEDED(hresReturn)) {
        MD_SET_DATA_RECORD_EXT(&mdrData,
                               dwPasswordMetaId,
                               METADATA_INHERIT | METADATA_SECURE,
                               IIS_MD_UT_FILE,
                               STRING_METADATA,
                               sizeof(pstPasswordBuf),
                               (PBYTE)pstPasswordBuf)

        hresReturn = pcCom->ComMDSetMetaData(mhOpenHandle,
                                             NULL,
                                             &mdrData);

        pcCom->ComMDCloseMetaObject(mhOpenHandle);
    }

    return SUCCEEDED(hresReturn);
}

BOOL DoesUserExist( LPWSTR strUsername, BOOL *fDisabled )
{
    BYTE *pBuffer;
    INT err = NERR_Success;
    BOOL fReturn = FALSE;

    *fDisabled = FALSE;

    err = NetUserGetInfo( NULL, strUsername, 3, &pBuffer );

    if ( err == NERR_Success )
    {
        *fDisabled = !!(((PUSER_INFO_3)pBuffer)->usri3_flags  & UF_ACCOUNTDISABLE);
        NetApiBufferFree( pBuffer );
        fReturn = TRUE;
    }

    return( fReturn );
}


NET_API_STATUS
NetpNtStatusToApiStatus (
    IN NTSTATUS NtStatus
    )

/*++

Routine Description:

    This function takes an NT status code and maps it to the appropriate
    LAN Man error code.

Arguments:

    NtStatus - Supplies the NT status.

Return Value:

    Returns the appropriate LAN Man error code for the NT status.

--*/
{
    NET_API_STATUS error;

    //
    // A small optimization for the most common case.
    //
    if ( NtStatus == STATUS_SUCCESS ) {
        return NERR_Success;
    }


    switch ( NtStatus ) {

        case STATUS_BUFFER_TOO_SMALL :
            return NERR_BufTooSmall;

        case STATUS_FILES_OPEN :
            return NERR_OpenFiles;

        case STATUS_CONNECTION_IN_USE :
            return NERR_DevInUse;

        case STATUS_INVALID_LOGON_HOURS :
            return NERR_InvalidLogonHours;

        case STATUS_INVALID_WORKSTATION :
            return NERR_InvalidWorkstation;

        case STATUS_PASSWORD_EXPIRED :
            return NERR_PasswordExpired;

        case STATUS_ACCOUNT_EXPIRED :
            return NERR_AccountExpired;

        case STATUS_REDIRECTOR_NOT_STARTED :
            return NERR_NetNotStarted;

        case STATUS_GROUP_EXISTS:
                return NERR_GroupExists;

        case STATUS_INTERNAL_DB_CORRUPTION:
                return NERR_InvalidDatabase;

        case STATUS_INVALID_ACCOUNT_NAME:
                return NERR_BadUsername;

        case STATUS_INVALID_DOMAIN_ROLE:
        case STATUS_INVALID_SERVER_STATE:
        case STATUS_BACKUP_CONTROLLER:
                return NERR_NotPrimary;

        case STATUS_INVALID_DOMAIN_STATE:
                return NERR_ACFNotLoaded;

        case STATUS_MEMBER_IN_GROUP:
                return NERR_UserInGroup;

        case STATUS_MEMBER_NOT_IN_GROUP:
                return NERR_UserNotInGroup;

        case STATUS_NONE_MAPPED:
        case STATUS_NO_SUCH_GROUP:
                return NERR_GroupNotFound;

        case STATUS_SPECIAL_GROUP:
        case STATUS_MEMBERS_PRIMARY_GROUP:
                return NERR_SpeGroupOp;

        case STATUS_USER_EXISTS:
                return NERR_UserExists;

        case STATUS_NO_SUCH_USER:
                return NERR_UserNotFound;

        case STATUS_PRIVILEGE_NOT_HELD:
                return ERROR_ACCESS_DENIED;

        case STATUS_LOGON_SERVER_CONFLICT:
                return NERR_LogonServerConflict;

        case STATUS_TIME_DIFFERENCE_AT_DC:
                return NERR_TimeDiffAtDC;

        case STATUS_SYNCHRONIZATION_REQUIRED:
                return NERR_SyncRequired;

        case STATUS_WRONG_PASSWORD_CORE:
                return NERR_BadPasswordCore;

        case STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
                return NERR_DCNotFound;

        case STATUS_PASSWORD_RESTRICTION:
                return NERR_PasswordTooShort;

        case STATUS_ALREADY_DISCONNECTED:
                return NERR_Success;

        default:

            //
            // Use the system routine to do the mapping to ERROR_ codes.
            //

#ifndef WIN32_CHICAGO 
            error = RtlNtStatusToDosError( NtStatus );

            if ( error != (NET_API_STATUS)NtStatus ) {
                return error;
            }
#endif // WIN32_CHICAGO

            //
            // Could not map the NT status to anything appropriate.
            //

            return NERR_InternalError;
    }
} // NetpNtStatusToApiStatus


NET_API_STATUS
UaspGetDomainId(
    IN LPCWSTR ServerName OPTIONAL,
    OUT PSAM_HANDLE SamServerHandle OPTIONAL,
    OUT PPOLICY_ACCOUNT_DOMAIN_INFO * AccountDomainInfo
    )
/*++
Routine Description:
    Return a domain ID of the account domain of a server.
Arguments:
    ServerName - A pointer to a string containing the name of the
        Domain Controller (DC) to query.  A NULL pointer
        or string specifies the local machine.
    SamServerHandle - Returns the SAM connection handle if the caller wants it.
    DomainId - Receives a pointer to the domain ID.
        Caller must deallocate buffer using NetpMemoryFree.
Return Value:
    Error code for the operation.
--*/
{
    NET_API_STATUS NetStatus;
    NTSTATUS Status;

    SAM_HANDLE LocalSamHandle = NULL;

    ACCESS_MASK LSADesiredAccess;
    LSA_HANDLE  LSAPolicyHandle = NULL;
    OBJECT_ATTRIBUTES LSAObjectAttributes;

    UNICODE_STRING ServerNameString;


    //
    // Connect to the SAM server
    //
    RtlInitUnicodeString( &ServerNameString, ServerName );

    Status = SamConnect(
                &ServerNameString,
                &LocalSamHandle,
                SAM_SERVER_LOOKUP_DOMAIN,
                NULL);

    if ( !NT_SUCCESS(Status))
    {
        LocalSamHandle = NULL;
        NetStatus = NetpNtStatusToApiStatus( Status );
        goto Cleanup;
    }


    //
    // Open LSA to read account domain info.
    //

    if ( AccountDomainInfo != NULL) {
        //
        // set desired access mask.
        //
        LSADesiredAccess = POLICY_VIEW_LOCAL_INFORMATION;

        InitializeObjectAttributes( &LSAObjectAttributes,
                                      NULL,             // Name
                                      0,                // Attributes
                                      NULL,             // Root
                                      NULL );           // Security Descriptor

        Status = LsaOpenPolicy( &ServerNameString,
                                &LSAObjectAttributes,
                                LSADesiredAccess,
                                &LSAPolicyHandle );

        if( !NT_SUCCESS(Status) ) {
            NetStatus = NetpNtStatusToApiStatus( Status );
            goto Cleanup;
        }


        //
        // now read account domain info from LSA.
        //

        Status = LsaQueryInformationPolicy(
                        LSAPolicyHandle,
                        PolicyAccountDomainInformation,
                        (PVOID *) AccountDomainInfo );

        if( !NT_SUCCESS(Status) ) {
            NetStatus = NetpNtStatusToApiStatus( Status );
            goto Cleanup;
        }
    }

    //
    // Return the SAM connection handle to the caller if he wants it.
    // Otherwise, disconnect from SAM.
    //

    if ( ARGUMENT_PRESENT( SamServerHandle ) ) {
        *SamServerHandle = LocalSamHandle;
        LocalSamHandle = NULL;
    }

    NetStatus = NERR_Success;

    //
    // Cleanup locally used resources
    //
Cleanup:
    if ( LocalSamHandle != NULL ) {
        (VOID) SamCloseHandle( LocalSamHandle );
    }

    if( LSAPolicyHandle != NULL ) {
        LsaClose( LSAPolicyHandle );
    }

    return NetStatus;
} // UaspGetDomainId


NET_API_STATUS
SampCreateFullSid(
    IN PSID DomainSid,
    IN ULONG Rid,
    OUT PSID *AccountSid
    )
/*++
Routine Description:
    This function creates a domain account sid given a domain sid and
    the relative id of the account within the domain.
    The returned Sid may be freed with LocalFree.
--*/
{
    NET_API_STATUS NetStatus;
    NTSTATUS    IgnoreStatus;
    UCHAR       AccountSubAuthorityCount;
    ULONG       AccountSidLength;
    PULONG      RidLocation;

    //
    // Calculate the size of the new sid
    //
    AccountSubAuthorityCount = *RtlSubAuthorityCountSid(DomainSid) + (UCHAR)1;
    AccountSidLength = RtlLengthRequiredSid(AccountSubAuthorityCount);

    //
    // Allocate space for the account sid
    //
    *AccountSid = LocalAlloc(LMEM_ZEROINIT,AccountSidLength);
    if (*AccountSid == NULL)
    {
        NetStatus = ERROR_NOT_ENOUGH_MEMORY;
    }
    else
    {
        //
        // Copy the domain sid into the first part of the account sid
        //
        IgnoreStatus = RtlCopySid(AccountSidLength, *AccountSid, DomainSid);
        ASSERT(NT_SUCCESS(IgnoreStatus));

        //
        // Increment the account sid sub-authority count
        //
        *RtlSubAuthorityCountSid(*AccountSid) = AccountSubAuthorityCount;

        //
        // Add the rid as the final sub-authority
        //
        RidLocation = RtlSubAuthoritySid(*AccountSid, AccountSubAuthorityCount-1);
        *RidLocation = Rid;

        NetStatus = NERR_Success;
    }

    return(NetStatus);
}



int GetGuestUserNameForDomain_FastWay(LPTSTR szDomainToLookUp,LPTSTR lpGuestUsrName)
{
    int iReturn = FALSE;
    NET_API_STATUS NetStatus;

    // for UaspGetDomainId()
    SAM_HANDLE SamServerHandle = NULL;
    PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo = NULL;

    PSID pAccountSid = NULL;
    PSID pDomainSid = NULL;

    // for LookupAccountSid()
    SID_NAME_USE sidNameUse = SidTypeUser;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    TCHAR szUserName[UNLEN+1];
    DWORD cbName = UNLEN+1;
    // use UNLEN because DNLEN is too small
    TCHAR szReferencedDomainName[UNLEN+1];
    DWORD cbReferencedDomainName = sizeof(szReferencedDomainName);

    // make sure not to return back gobble-d-gook
    wcscpy(lpGuestUsrName, TEXT(""));

    //
    // Get the Sid for the specified Domain
    //
    // szDomainToLookUp=NULL for local machine
    NetStatus = UaspGetDomainId( szDomainToLookUp,&SamServerHandle,&pAccountDomainInfo );
    if ( NetStatus != NERR_Success )
    {
        goto GetGuestUserNameForDomain_FastWay_Exit;
    }
    pDomainSid = pAccountDomainInfo->DomainSid;
    //
    // Use the Domain Sid and the well known Guest RID to create the Real Guest Sid
    //
    // Well-known users ...
    // DOMAIN_USER_RID_ADMIN          (0x000001F4L)
    // DOMAIN_USER_RID_GUEST          (0x000001F5L)
    NetStatus = NERR_InternalError;
    NetStatus = SampCreateFullSid(pDomainSid, DOMAIN_USER_RID_GUEST, &pAccountSid);
    if ( NetStatus != NERR_Success )
    {
        goto GetGuestUserNameForDomain_FastWay_Exit;
    }

    //
    // Check if the SID is valid
    //
    if (0 == IsValidSid(pAccountSid))
    {
        DWORD dwErr = GetLastError();
        goto GetGuestUserNameForDomain_FastWay_Exit;
    }

    //
    // Retrieve the UserName for the specified SID
    //
    wcscpy(szUserName, TEXT(""));
    wcscpy(szReferencedDomainName, TEXT(""));
    // szDomainToLookUp=NULL for local machine
    if (!LookupAccountSid(szDomainToLookUp,
                          pAccountSid,
                          szUserName,
                          &cbName,
                          szReferencedDomainName,
                          &cbReferencedDomainName,
                          &sidNameUse))
    {
        DWORD dwErr = GetLastError();
        goto GetGuestUserNameForDomain_FastWay_Exit;
    }


    // Return the guest user name that we got.
    wcscpy(lpGuestUsrName, szUserName);

    // Wow, after all that, we must have succeeded
    iReturn = TRUE;

GetGuestUserNameForDomain_FastWay_Exit:
    // Free the Domain info if we got some
    if (pAccountDomainInfo) {NetpMemoryFree(pAccountDomainInfo);}
    // Free the sid if we had allocated one
    if (pAccountSid) {LocalFree(pAccountSid);}
    return iReturn;
}

int GetGuestUserName_SlowWay(LPWSTR lpGuestUsrName)
{

    LPWSTR ServerName = NULL; // default to local machine
    DWORD Level = 1; // to retrieve info of all local and global normal user accounts
    DWORD Index = 0;
    DWORD EntriesRequested = 5;
    DWORD PreferredMaxLength = 1024;
    DWORD ReturnedEntryCount = 0;
    PVOID SortedBuffer = NULL;
    NET_DISPLAY_USER *p = NULL;
    DWORD i=0;
    int err = 0;
    BOOL fStatus = TRUE;

    while (fStatus)
    {
        err = NetQueryDisplayInformation(ServerName,
                                         Level,
                                         Index,
                                         EntriesRequested,
                                         PreferredMaxLength,
                                         &ReturnedEntryCount,
                                         &SortedBuffer);
        if (err == NERR_Success)
            fStatus = FALSE;
        if (err == NERR_Success || err == ERROR_MORE_DATA)
        {
            p = (NET_DISPLAY_USER *)SortedBuffer;
            i = 0;
            while (i < ReturnedEntryCount && (p[i].usri1_user_id != DOMAIN_USER_RID_GUEST))
                i++;
            if (i == ReturnedEntryCount)
            {
                if (err == ERROR_MORE_DATA)
                { // need to get more entries
                    Index = p[i-1].usri1_next_index;
                }
            }
            else
            {
                wcscpy(lpGuestUsrName, p[i].usri1_name);
                fStatus = FALSE;
            }
        }
        NetApiBufferFree(SortedBuffer);
    }

    return 0;
}

void GetGuestUserName(LPTSTR lpOutGuestUsrName)
{
    // try to retrieve the guest username the fast way
    // meaning = lookup the domain sid, and the well known guest rid, to get the guest sid.
    // then look it up.  The reason for this function is that on large domains with mega users
    // the account can be quickly looked up.
    TCHAR szGuestUsrName[UNLEN+1];
    LPTSTR pszComputerName = NULL;
    if (!GetGuestUserNameForDomain_FastWay(pszComputerName,szGuestUsrName))
    {

        // if the fast way failed for some reason, then let's do it
        // the slow way, since this way always used to work, only on large domains (1 mil users)
        // it could take 24hrs (since this function actually enumerates thru the domain)
        GetGuestUserName_SlowWay(szGuestUsrName);
    }

    // Return back the username
    wcscpy(lpOutGuestUsrName,szGuestUsrName);
    return;
}

int GetGuestGrpName(LPTSTR lpGuestGrpName)
{
    LPCTSTR ServerName = NULL; // local machine
    // use UNLEN because DNLEN is too small
    DWORD cbName = UNLEN+1;
    TCHAR ReferencedDomainName[UNLEN+1];
    DWORD cbReferencedDomainName = sizeof(ReferencedDomainName);
    SID_NAME_USE sidNameUse = SidTypeUser;

    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    PSID GuestsSid = NULL;

    AllocateAndInitializeSid(&NtAuthority,
                             2,
                             SECURITY_BUILTIN_DOMAIN_RID,
                             DOMAIN_ALIAS_RID_GUESTS,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &GuestsSid);

    LookupAccountSid(ServerName,
                     GuestsSid,
                     lpGuestGrpName,
                     &cbName,
                     ReferencedDomainName,
                     &cbReferencedDomainName,
                     &sidNameUse);

    if (GuestsSid)
        FreeSid(GuestsSid);

    return 0;
}

INT RegisterAccountToLocalGroup(LPCTSTR szAccountName,
                                LPCTSTR szLocalGroupName,
                                BOOL fAction)
{
    int err;

    // get the sid of szAccountName
    PSID pSID = NULL;
    BOOL bWellKnownSID = FALSE;
    err = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);
    if (err != ERROR_SUCCESS)
    {
        return (err);
    }

    // Get the localized LocalGroupName
    TCHAR szLocalizedLocalGroupName[GNLEN + 1];
    if (_wcsicmp(szLocalGroupName, TEXT("Guests")) == 0)
    {
        GetGuestGrpName(szLocalizedLocalGroupName);
    }
    else
    {
        wcscpy(szLocalizedLocalGroupName, szLocalGroupName);
    }

    // transfer szLocalGroupName to WCHAR
    WCHAR wszLocalGroupName[_MAX_PATH];
    wcscpy(wszLocalGroupName, szLocalizedLocalGroupName);

    LOCALGROUP_MEMBERS_INFO_0 buf;

    buf.lgrmi0_sid = pSID;

    if (fAction)
    {
        err = NetLocalGroupAddMembers(NULL, wszLocalGroupName, 0, (LPBYTE)&buf, 1);
    }
    else
    {
        err = NetLocalGroupDelMembers(NULL, wszLocalGroupName, 0, (LPBYTE)&buf, 1);
    }

    if (pSID)
    {
        if (bWellKnownSID)
            FreeSid (pSID);
        else
            free (pSID);
    }

    return (err);
}

void InitLsaString(PLSA_UNICODE_STRING LsaString,LPWSTR String)
{
    DWORD StringLength;

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

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

DWORD OpenPolicy(LPTSTR ServerName,DWORD DesiredAccess,PLSA_HANDLE PolicyHandle)
{
    DWORD Error;
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server = NULL;
    SECURITY_QUALITY_OF_SERVICE QualityOfService;

    QualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
    QualityOfService.ImpersonationLevel = SecurityImpersonation;
    QualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    QualityOfService.EffectiveOnly = FALSE;

    //
    // The two fields that must be set are length and the quality of service.
    //
    ObjectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
    ObjectAttributes.RootDirectory = NULL;
    ObjectAttributes.ObjectName = NULL;
    ObjectAttributes.Attributes = 0;
    ObjectAttributes.SecurityDescriptor = NULL;
    ObjectAttributes.SecurityQualityOfService = &QualityOfService;

    if (ServerName != NULL)
    {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //
        InitLsaString(&ServerString,ServerName);
        Server = &ServerString;
    }
    //
    // Attempt to open the policy for all access
    //
    Error = LsaOpenPolicy(Server,&ObjectAttributes,DesiredAccess,PolicyHandle);
    return(Error);

}


INT RegisterAccountUserRights(LPCTSTR szAccountName, BOOL fAction, BOOL fSpecicaliWamAccount)
{
    int err;

    // get the sid of szAccountName
    PSID pSID = NULL;
    BOOL bWellKnownSID = FALSE;
    err = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);
    if (err != ERROR_SUCCESS)
    {
        return (err);
    }

    LSA_UNICODE_STRING UserRightString;
    LSA_HANDLE PolicyHandle = NULL;

    err = OpenPolicy(NULL, POLICY_ALL_ACCESS,&PolicyHandle);
    if ( err == NERR_Success )
    {
        if (fAction) 
        {
// defined in ntsecapi.h and ntlsa.h
//#define SE_INTERACTIVE_LOGON_NAME       TEXT("SeInteractiveLogonRight")
//#define SE_NETWORK_LOGON_NAME           TEXT("SeNetworkLogonRight")
//#define SE_BATCH_LOGON_NAME             TEXT("SeBatchLogonRight")
//#define SE_SERVICE_LOGON_NAME           TEXT("SeServiceLogonRight")
// Defined in winnt.h
//#define SE_CREATE_TOKEN_NAME              TEXT("SeCreateTokenPrivilege")
//#define SE_ASSIGNPRIMARYTOKEN_NAME        TEXT("SeAssignPrimaryTokenPrivilege")
//#define SE_LOCK_MEMORY_NAME               TEXT("SeLockMemoryPrivilege")
//#define SE_INCREASE_QUOTA_NAME            TEXT("SeIncreaseQuotaPrivilege")
//#define SE_UNSOLICITED_INPUT_NAME         TEXT("SeUnsolicitedInputPrivilege")
//#define SE_MACHINE_ACCOUNT_NAME           TEXT("SeMachineAccountPrivilege")
//#define SE_TCB_NAME                       TEXT("SeTcbPrivilege")
//#define SE_SECURITY_NAME                  TEXT("SeSecurityPrivilege")
//#define SE_TAKE_OWNERSHIP_NAME            TEXT("SeTakeOwnershipPrivilege")
//#define SE_LOAD_DRIVER_NAME               TEXT("SeLoadDriverPrivilege")
//#define SE_SYSTEM_PROFILE_NAME            TEXT("SeSystemProfilePrivilege")
//#define SE_SYSTEMTIME_NAME                TEXT("SeSystemtimePrivilege")
//#define SE_PROF_SINGLE_PROCESS_NAME       TEXT("SeProfileSingleProcessPrivilege")
//#define SE_INC_BASE_PRIORITY_NAME         TEXT("SeIncreaseBasePriorityPrivilege")
//#define SE_CREATE_PAGEFILE_NAME           TEXT("SeCreatePagefilePrivilege")
//#define SE_CREATE_PERMANENT_NAME          TEXT("SeCreatePermanentPrivilege")
//#define SE_BACKUP_NAME                    TEXT("SeBackupPrivilege")
//#define SE_RESTORE_NAME                   TEXT("SeRestorePrivilege")
//#define SE_SHUTDOWN_NAME                  TEXT("SeShutdownPrivilege")
//#define SE_DEBUG_NAME                     TEXT("SeDebugPrivilege")
//#define SE_AUDIT_NAME                     TEXT("SeAuditPrivilege")
//#define SE_SYSTEM_ENVIRONMENT_NAME        TEXT("SeSystemEnvironmentPrivilege")
//#define SE_CHANGE_NOTIFY_NAME             TEXT("SeChangeNotifyPrivilege")
//#define SE_REMOTE_SHUTDOWN_NAME           TEXT("SeRemoteShutdownPrivilege")
//#define SE_UNDOCK_NAME                    TEXT("SeUndockPrivilege")
//#define SE_SYNC_AGENT_NAME                TEXT("SeSyncAgentPrivilege")
//#define SE_ENABLE_DELEGATION_NAME         TEXT("SeEnableDelegationPrivilege")
            if (fSpecicaliWamAccount)
            {
                // no interactive logon for iwam!
                InitLsaString(&UserRightString, SE_NETWORK_LOGON_NAME);
                err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
                InitLsaString(&UserRightString, SE_BATCH_LOGON_NAME);
                err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
            }
            else
            {
                InitLsaString(&UserRightString, SE_INTERACTIVE_LOGON_NAME);
                err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
                InitLsaString(&UserRightString, SE_NETWORK_LOGON_NAME);
                err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
                InitLsaString(&UserRightString, SE_BATCH_LOGON_NAME);
                err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
            }
        }
        else 
        {
            InitLsaString(&UserRightString, SE_INTERACTIVE_LOGON_NAME);
            err = LsaRemoveAccountRights(PolicyHandle, pSID, FALSE, &UserRightString,1);
            InitLsaString(&UserRightString, SE_NETWORK_LOGON_NAME);
            err = LsaRemoveAccountRights(PolicyHandle, pSID, FALSE, &UserRightString,1);
            InitLsaString(&UserRightString, SE_BATCH_LOGON_NAME);
            err = LsaRemoveAccountRights(PolicyHandle, pSID, FALSE, &UserRightString,1);
        }

        LsaClose(PolicyHandle);
    }

    if (pSID)
    {
        if (bWellKnownSID)
            FreeSid (pSID);
        else
            free (pSID);
    }

    return (err);
}


int ChangeUserPassword(IN LPTSTR szUserName, IN LPTSTR szNewPassword)
{
    int iReturn = TRUE;
    USER_INFO_1003  pi1003; 
    NET_API_STATUS  nas; 

    TCHAR szRawComputerName[CNLEN + 10];
    DWORD dwLen = CNLEN + 10;
    TCHAR szComputerName[CNLEN + 10];
    TCHAR szCopyOfUserName[UNLEN+10];
    TCHAR szTempFullUserName[(CNLEN + 10) + (UNLEN+1)];
    LPTSTR pch = NULL;

    wcscpy(szCopyOfUserName, szUserName);

    //iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("ChangeUserPassword().Start.name=%s,pass=%s"),szCopyOfUserName,szNewPassword));

    if ( !GetComputerName( szRawComputerName, &dwLen ))
        {goto ChangeUserPassword_Exit;}

    // Make a copy to be sure not to move the pointer around.
    wcscpy(szTempFullUserName, szCopyOfUserName);
    // Check if there is a "\" in there.
    pch = wcschr(szTempFullUserName, '\\');
    if (pch) 
        {
            // szCopyOfUserName should now go from something like this:
            // mycomputer\myuser
            // to this myuser
            wcscpy(szCopyOfUserName,pch+1);
            // trim off the '\' character to leave just the domain\computername so we can check against it.
            *pch = '\0';
            // compare the szTempFullUserName with the local computername.
            if (0 == _wcsicmp(szRawComputerName, szTempFullUserName))
            {
                // the computername\username has a hardcoded computername in it.
                // lets try to get only the username
                // look szCopyOfusername is already set
            }
            else
            {
                // the local computer machine name
                // and the specified username are different, so get out
                // and don't even try to change this user\password since
                // it's probably a domain\username

                // return true -- saying that we did in fact change the passoword.
                // we really didn't but we can't
                iReturn = TRUE;
                goto ChangeUserPassword_Exit;
            }
        }

    // Make sure the computername has a \\ in front of it
    if ( szRawComputerName[0] != '\\' )
        {wcscpy(szComputerName,L"\\\\");}
    wcscat(szComputerName,szRawComputerName);
    // 
    // administrative over-ride of existing password 
    // 
    // by this time szCopyOfUserName
    // should not look like mycomputername\username but it should look like username.
    pi1003.usri1003_password = szNewPassword;
     nas = NetUserSetInfo(
            szComputerName,   // computer name 
            szCopyOfUserName, // username 
            1003,             // info level 
            (LPBYTE)&pi1003,  // new info 
            NULL 
            ); 

    if(nas != NERR_Success) 
    {
        iReturn = FALSE;
        goto ChangeUserPassword_Exit;
    }

ChangeUserPassword_Exit:
    //iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("ChangeUserPassword().End.Ret=%d"),iReturn));
    return iReturn; 
} 


//
// Create InternetGuest Account
//
BOOL CreateUser( LPCTSTR szUsername,
                LPCTSTR szPassword,
                LPCTSTR szComment,
                LPCTSTR szFullName,
                BOOL fSpecialiWamAccount
                )
{

    INT err = NERR_Success;

    BYTE *pBuffer;
    WCHAR defGuest[UNLEN+1];
    TCHAR defGuestGroup[GNLEN+1];
    WCHAR wchGuestGroup[GNLEN+1];
    WCHAR wchUsername[UNLEN+1];
    WCHAR wchPassword[LM20_PWLEN+1];

    GetGuestUserName(defGuest);

    GetGuestGrpName(defGuestGroup);

    memset((PVOID)wchUsername, 0, sizeof(wchUsername));
    memset((PVOID)wchPassword, 0, sizeof(wchPassword));
    wcsncpy(wchGuestGroup, defGuestGroup, GNLEN);
    wcsncpy(wchUsername, szUsername, UNLEN);
    wcsncpy(wchPassword, szPassword, LM20_PWLEN);

    err = NetUserGetInfo( NULL, defGuest, 3, &pBuffer );

    if ( err == NERR_Success )
    {
        do
        {
            WCHAR wchComment[MAXCOMMENTSZ+1];
            WCHAR wchFullName[UNLEN+1];

            memset((PVOID)wchComment, 0, sizeof(wchComment));
            memset((PVOID)wchFullName, 0, sizeof(wchFullName));
            wcsncpy(wchComment, szComment, MAXCOMMENTSZ);
            wcsncpy(wchFullName, szFullName, UNLEN);

            USER_INFO_3 *lpui3 = (USER_INFO_3 *)pBuffer;

            lpui3->usri3_name = wchUsername;
            lpui3->usri3_password = wchPassword;
            lpui3->usri3_flags &= ~ UF_ACCOUNTDISABLE;
            lpui3->usri3_flags |= UF_DONT_EXPIRE_PASSWD;
            lpui3->usri3_acct_expires = TIMEQ_FOREVER;

            lpui3->usri3_comment = wchComment;
            lpui3->usri3_usr_comment = wchComment;
            lpui3->usri3_full_name = wchFullName;
            lpui3->usri3_primary_group_id = DOMAIN_GROUP_RID_USERS;

            DWORD parm_err;

            err = NetUserAdd( NULL, 3, pBuffer, &parm_err );

            if ( err != NERR_Success )
            {
              if ( err == NERR_UserExists )
              {
                  // see if we can just change the password.
                  if (TRUE == ChangeUserPassword((LPTSTR) szUsername, (LPTSTR) szPassword))
                     {err = NERR_Success;}
              }
              else
              {
                break;
              }
            }


        } while (FALSE);

        NetApiBufferFree( pBuffer );
    }
    if ( err == NERR_Success )
    {
        // add it to the guests or IIS_WPG group
        if (fSpecialiWamAccount)
        {
            RegisterAccountToLocalGroup(szUsername, IIS_WP_GROUP, TRUE);
        }
        else
        {
            RegisterAccountToLocalGroup(szUsername, TEXT("Guests"), TRUE);
        }

        // add certain user rights to this account
        RegisterAccountUserRights(szUsername, TRUE, fSpecialiWamAccount);
    }

    return err == NERR_Success;
}

INT DeleteGuestUser( LPCTSTR szUsername )
{

    INT err = NERR_Success;
    BYTE *pBuffer;
    BOOL fDisabled;

    WCHAR wchUsername[UNLEN+1];

    wcsncpy(wchUsername, szUsername, UNLEN);

    if (FALSE == DoesUserExist(wchUsername,&fDisabled))
    {
        return err;
    }

    // remove it from the guests group
    RegisterAccountToLocalGroup(szUsername, TEXT("Guests"), FALSE);

    // remove certain user rights of this account
    RegisterAccountUserRights(szUsername, FALSE, TRUE);

    err = ::NetUserDel( TEXT(""), wchUsername );

    return err;
}

#define MAX_REALISTIC_RESOURCE_LEN  MAX_PATH
BOOL CreateUserAccount(LPTSTR pszAnonyName,
                       LPTSTR pszAnonyPass,
                       DWORD  dwUserCommentResourceId,
                       DWORD  dwUserFullNameResourceId,
                       BOOL   fSpecicaliWamAccount
                       )
{

    BOOL fReturn = FALSE;
    WCHAR pszComment[MAX_REALISTIC_RESOURCE_LEN];
    WCHAR pszFullName[MAX_REALISTIC_RESOURCE_LEN];

    //
    // First Load the Resources
    //

    HMODULE hBinary;

    hBinary = GetModuleHandle(TEXT("svcext"));

    if (hBinary != NULL) {
        fReturn = LoadString(hBinary,
                             dwUserCommentResourceId,
                             pszComment,
                             MAX_REALISTIC_RESOURCE_LEN);
        if (fReturn) {
            fReturn = LoadString(hBinary,
                                 dwUserFullNameResourceId,
                                 pszFullName,
                                 MAX_REALISTIC_RESOURCE_LEN);
        }

    }

    if (fReturn) {
        fReturn = CreateUser(pszAnonyName,
                             pszAnonyPass,
                             pszComment,
                             pszFullName,
                             fSpecicaliWamAccount
                             );
    }

    if (fReturn) {
        ChangeDCOMLaunchACL(pszAnonyName,
                            TRUE,
                            TRUE);
        /* removed when fixing bug 355249
        ChangeDCOMAccessACL(pszAnonyName,
                            TRUE,
                            TRUE);
        */        
    }

    return fReturn;
}

typedef void (*P_SslGenerateRandomBits)( PUCHAR pRandomData, LONG size );
P_SslGenerateRandomBits ProcSslGenerateRandomBits = NULL;

int GetRandomNum(void)
{
    int RandomNum;
    UCHAR cRandomByte;

    if ( ProcSslGenerateRandomBits != NULL )
    {
        (*ProcSslGenerateRandomBits)( &cRandomByte, 1 );
        RandomNum = cRandomByte;
    } else
    {
        RandomNum = rand();
    }

    return(RandomNum);
}

void ShuffleCharArray(int iSizeOfTheArray, TCHAR * lptsTheArray)
{
    int i;
    int iTotal;
    int RandomNum;

    iTotal = iSizeOfTheArray / sizeof(TCHAR);
    for (i=0; i<iTotal;i++ )
    {
        // shuffle the array
        RandomNum=GetRandomNum();
        TCHAR c = lptsTheArray[i];
        lptsTheArray[i]=lptsTheArray[RandomNum%iTotal];
        lptsTheArray[RandomNum%iTotal]=c;
    }
    return;
}

//
// Create a random password
//
void CreatePassword( TCHAR *pszPassword )
{
    //
    // Use Maximum available password length, as
    // setting any other length might run afoul
    // of the minimum password length setting
    //
    int nLength = LM20_PWLEN;
    int iTotal = 0;
    int RandomNum = 0;
    int i;
    TCHAR six2pr[64] =
    {
        TEXT('A'), TEXT('B'), TEXT('C'), TEXT('D'), TEXT('E'), TEXT('F'), TEXT('G'), TEXT('H'),
        TEXT('I'), TEXT('J'), TEXT('K'), TEXT('L'), TEXT('M'),
        TEXT('N'), TEXT('O'), TEXT('P'), TEXT('Q'), TEXT('R'), TEXT('S'), TEXT('T'), TEXT('U'),
        TEXT('V'), TEXT('W'), TEXT('X'), TEXT('Y'), TEXT('Z'),
        TEXT('a'), TEXT('b'), TEXT('c'), TEXT('d'), TEXT('e'), TEXT('f'), TEXT('g'), TEXT('h'),
        TEXT('i'), TEXT('j'), TEXT('k'), TEXT('l'), TEXT('m'),
        TEXT('n'), TEXT('o'), TEXT('p'), TEXT('q'), TEXT('r'), TEXT('s'), TEXT('t'), TEXT('u'),
        TEXT('v'), TEXT('w'), TEXT('x'), TEXT('y'), TEXT('z'),
        TEXT('0'), TEXT('1'), TEXT('2'), TEXT('3'), TEXT('4'), TEXT('5'), TEXT('6'), TEXT('7'),
        TEXT('8'), TEXT('9'), TEXT('*'), TEXT('_')
    };

    // create a random password
    ProcSslGenerateRandomBits = NULL;

    HINSTANCE hSslDll = LoadLibraryEx(TEXT("schannel.dll"), NULL, 0 );
    if ( hSslDll )
        {
        ProcSslGenerateRandomBits = (P_SslGenerateRandomBits)GetProcAddress( hSslDll,
                                                                             "SslGenerateRandomBits");
        }

    // See the random number generation for rand() call in GetRandomNum()
    time_t timer;
    time( &timer );
    srand( (unsigned int) timer );

    // shuffle around the global six2pr[] array
    ShuffleCharArray(sizeof(six2pr), (TCHAR*) &six2pr);
    // assign each character of the password array
    iTotal = sizeof(six2pr) / sizeof(TCHAR);
    for ( i=0;i<nLength;i++ )
    {
        RandomNum=GetRandomNum();
        pszPassword[i]=six2pr[RandomNum%iTotal];
    }

    //
    // in order to meet a possible
    // policy set upon passwords..
    //
    // replace the last 4 chars with these:
    //
    // 1) something from !@#$%^&*()-+=
    // 2) something from 1234567890
    // 3) an uppercase letter
    // 4) a lowercase letter
    //
    TCHAR something1[12] = {TEXT('!'), TEXT('@'), TEXT('#'), TEXT('$'), TEXT('^'), TEXT('&'),
                            TEXT('*'), TEXT('('), TEXT(')'), TEXT('-'), TEXT('+'), TEXT('=')};
    ShuffleCharArray(sizeof(something1), (TCHAR*) &something1);
    TCHAR something2[10] = {TEXT('0'), TEXT('1'), TEXT('2'), TEXT('3'), TEXT('4'), TEXT('5'),
                            TEXT('6'), TEXT('7'), TEXT('8'), TEXT('9')};
    ShuffleCharArray(sizeof(something2),(TCHAR*) &something2);
    TCHAR something3[26] = {TEXT('A'), TEXT('B'), TEXT('C'), TEXT('D'), TEXT('E'), TEXT('F'),
                            TEXT('G'), TEXT('H'), TEXT('I'), TEXT('J'), TEXT('K'), TEXT('L'),
                            TEXT('M'), TEXT('N'), TEXT('O'), TEXT('P'), TEXT('Q'), TEXT('R'),
                            TEXT('S'), TEXT('T'), TEXT('U'), TEXT('V'), TEXT('W'), TEXT('X'),
                            TEXT('Y'), TEXT('Z')};
    ShuffleCharArray(sizeof(something3),(TCHAR*) &something3);
    TCHAR something4[26] = {TEXT('a'), TEXT('b'), TEXT('c'), TEXT('d'), TEXT('e'), TEXT('f'),
                            TEXT('g'), TEXT('h'), TEXT('i'), TEXT('j'), TEXT('k'), TEXT('l'),
                            TEXT('m'), TEXT('n'), TEXT('o'), TEXT('p'), TEXT('q'), TEXT('r'),
                            TEXT('s'), TEXT('t'), TEXT('u'), TEXT('v'), TEXT('w'), TEXT('x'),
                            TEXT('y'), TEXT('z')};
    ShuffleCharArray(sizeof(something4),(TCHAR*)&something4);

    RandomNum=GetRandomNum();
    iTotal = sizeof(something1) / sizeof(TCHAR);
    pszPassword[nLength-4]=something1[RandomNum%iTotal];

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

    RandomNum=GetRandomNum();
    iTotal = sizeof(something3) / sizeof(TCHAR);
    pszPassword[nLength-2]=something3[RandomNum%iTotal];

    RandomNum=GetRandomNum();
    iTotal = sizeof(something4) / sizeof(TCHAR);
    pszPassword[nLength-1]=something4[RandomNum%iTotal];

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

    if (hSslDll)
        {FreeLibrary( hSslDll );}
}



BOOL ValidatePassword(IN LPCTSTR UserName,IN LPCTSTR Domain,IN LPCTSTR Password)
/*++
Routine Description:
    Uses SSPI to validate the specified password
Arguments:
    UserName - Supplies the user name
    Domain - Supplies the user's domain
    Password - Supplies the password
Return Value:
    TRUE if the password is valid.
    FALSE otherwise.
--*/
{
    SECURITY_STATUS SecStatus;
    SECURITY_STATUS AcceptStatus;
    SECURITY_STATUS InitStatus;
    CredHandle ClientCredHandle;
    CredHandle ServerCredHandle;
    BOOL ClientCredAllocated = FALSE;
    BOOL ServerCredAllocated = FALSE;
    CtxtHandle ClientContextHandle;
    CtxtHandle ServerContextHandle;
    TimeStamp Lifetime;
    ULONG ContextAttributes;
    PSecPkgInfo PackageInfo = NULL;
    ULONG ClientFlags;
    ULONG ServerFlags;
    TCHAR TargetName[100];
    SEC_WINNT_AUTH_IDENTITY_W AuthIdentity;
    BOOL Validated = FALSE;

    SecBufferDesc NegotiateDesc;
    SecBuffer NegotiateBuffer;

    SecBufferDesc ChallengeDesc;
    SecBuffer ChallengeBuffer;

    SecBufferDesc AuthenticateDesc;
    SecBuffer AuthenticateBuffer;

    AuthIdentity.User = (LPWSTR)UserName;
    AuthIdentity.UserLength = lstrlenW(UserName);
    AuthIdentity.Domain = (LPWSTR)Domain;
    AuthIdentity.DomainLength = lstrlenW(Domain);
    AuthIdentity.Password = (LPWSTR)Password;
    AuthIdentity.PasswordLength = lstrlenW(Password);
    AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

    NegotiateBuffer.pvBuffer = NULL;
    ChallengeBuffer.pvBuffer = NULL;
    AuthenticateBuffer.pvBuffer = NULL;

    //
    // Get info about the security packages.
    //

    SecStatus = QuerySecurityPackageInfo( TEXT("NTLM"), &PackageInfo );

    if ( SecStatus != STATUS_SUCCESS ) {
        goto error_exit;
    }

    //
    // Acquire a credential handle for the server side
    //
    SecStatus = AcquireCredentialsHandle(
                    NULL,
                    TEXT("NTLM"),
                    SECPKG_CRED_INBOUND,
                    NULL,
                    &AuthIdentity,
                    NULL,
                    NULL,
                    &ServerCredHandle,
                    &Lifetime );

    if ( SecStatus != STATUS_SUCCESS ) {
        goto error_exit;
    }
    ServerCredAllocated = TRUE;

    //
    // Acquire a credential handle for the client side
    //

    SecStatus = AcquireCredentialsHandle(
                    NULL,           // New principal
                    TEXT("NTLM"),
                    SECPKG_CRED_OUTBOUND,
                    NULL,
                    &AuthIdentity,
                    NULL,
                    NULL,
                    &ClientCredHandle,
                    &Lifetime );

    if ( SecStatus != STATUS_SUCCESS ) {
        goto error_exit;
    }
    ClientCredAllocated = TRUE;

    //
    // Get the NegotiateMessage (ClientSide)
    //

    NegotiateDesc.ulVersion = 0;
    NegotiateDesc.cBuffers = 1;
    NegotiateDesc.pBuffers = &NegotiateBuffer;

    NegotiateBuffer.cbBuffer = PackageInfo->cbMaxToken;
    NegotiateBuffer.BufferType = SECBUFFER_TOKEN;
    NegotiateBuffer.pvBuffer = LocalAlloc( 0, NegotiateBuffer.cbBuffer );
    if ( NegotiateBuffer.pvBuffer == NULL ) {
        goto error_exit;
    }

    ClientFlags = ISC_REQ_MUTUAL_AUTH | ISC_REQ_REPLAY_DETECT;

    InitStatus = InitializeSecurityContext(
                    &ClientCredHandle,
                    NULL,               // No Client context yet
                    NULL,
                    ClientFlags,
                    0,                  // Reserved 1
                    SECURITY_NATIVE_DREP,
                    NULL,                  // No initial input token
                    0,                  // Reserved 2
                    &ClientContextHandle,
                    &NegotiateDesc,
                    &ContextAttributes,
                    &Lifetime );

    if ( !NT_SUCCESS(InitStatus) ) {
        goto error_exit;
    }

    //
    // Get the ChallengeMessage (ServerSide)
    //

    NegotiateBuffer.BufferType |= SECBUFFER_READONLY;
    ChallengeDesc.ulVersion = 0;
    ChallengeDesc.cBuffers = 1;
    ChallengeDesc.pBuffers = &ChallengeBuffer;

    ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken;
    ChallengeBuffer.BufferType = SECBUFFER_TOKEN;
    ChallengeBuffer.pvBuffer = LocalAlloc( 0, ChallengeBuffer.cbBuffer );
    if ( ChallengeBuffer.pvBuffer == NULL ) {
        goto error_exit;
    }
    ServerFlags = ASC_REQ_EXTENDED_ERROR;

    AcceptStatus = AcceptSecurityContext(
                    &ServerCredHandle,
                    NULL,               // No Server context yet
                    &NegotiateDesc,
                    ServerFlags,
                    SECURITY_NATIVE_DREP,
                    &ServerContextHandle,
                    &ChallengeDesc,
                    &ContextAttributes,
                    &Lifetime );

    if ( !NT_SUCCESS(AcceptStatus) ) {
        goto error_exit;
    }

    if (InitStatus != STATUS_SUCCESS)
    {

        //
        // Get the AuthenticateMessage (ClientSide)
        //

        ChallengeBuffer.BufferType |= SECBUFFER_READONLY;
        AuthenticateDesc.ulVersion = 0;
        AuthenticateDesc.cBuffers = 1;
        AuthenticateDesc.pBuffers = &AuthenticateBuffer;

        AuthenticateBuffer.cbBuffer = PackageInfo->cbMaxToken;
        AuthenticateBuffer.BufferType = SECBUFFER_TOKEN;
        AuthenticateBuffer.pvBuffer = LocalAlloc( 0, AuthenticateBuffer.cbBuffer );
        if ( AuthenticateBuffer.pvBuffer == NULL ) {
            goto error_exit;
        }

        SecStatus = InitializeSecurityContext(
                        NULL,
                        &ClientContextHandle,
                        TargetName,
                        0,
                        0,                      // Reserved 1
                        SECURITY_NATIVE_DREP,
                        &ChallengeDesc,
                        0,                  // Reserved 2
                        &ClientContextHandle,
                        &AuthenticateDesc,
                        &ContextAttributes,
                        &Lifetime );

        if ( !NT_SUCCESS(SecStatus) ) {
            goto error_exit;
        }

        if (AcceptStatus != STATUS_SUCCESS) {

            //
            // Finally authenticate the user (ServerSide)
            //

            AuthenticateBuffer.BufferType |= SECBUFFER_READONLY;

            SecStatus = AcceptSecurityContext(
                            NULL,
                            &ServerContextHandle,
                            &AuthenticateDesc,
                            ServerFlags,
                            SECURITY_NATIVE_DREP,
                            &ServerContextHandle,
                            NULL,
                            &ContextAttributes,
                            &Lifetime );

            if ( !NT_SUCCESS(SecStatus) ) {
                goto error_exit;
            }
            Validated = TRUE;

        }

    }

error_exit:
    if (ServerCredAllocated) {
        FreeCredentialsHandle( &ServerCredHandle );
    }
    if (ClientCredAllocated) {
        FreeCredentialsHandle( &ClientCredHandle );
    }

    //
    // Final Cleanup
    //

    if ( NegotiateBuffer.pvBuffer != NULL ) {
        (VOID) LocalFree( NegotiateBuffer.pvBuffer );
    }

    if ( ChallengeBuffer.pvBuffer != NULL ) {
        (VOID) LocalFree( ChallengeBuffer.pvBuffer );
    }

    if ( AuthenticateBuffer.pvBuffer != NULL ) {
        (VOID) LocalFree( AuthenticateBuffer.pvBuffer );
    }
    return(Validated);
}



DWORD
ChangeAppIDAccessACL (
    LPTSTR AppID,
    LPTSTR Principal,
    BOOL SetPrincipal,
    BOOL Permit
    )
{
    TCHAR   keyName [256];
    DWORD   err;

    wcscpy(keyName, TEXT("APPID\\"));
    wcscat(keyName, AppID);

    if (SetPrincipal)
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
                                               keyName,
                                               TEXT("AccessPermission"),
                                               Principal);
        err = AddPrincipalToNamedValueSD (HKEY_CLASSES_ROOT,
                                          keyName,
                                          TEXT("AccessPermission"),
                                          Principal,
                                          Permit);
    }
    else
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
                                               keyName,
                                               TEXT("AccessPermission"),
                                               Principal);
    }

    return err;
}

DWORD
ChangeAppIDLaunchACL (
    LPTSTR AppID,
    LPTSTR Principal,
    BOOL SetPrincipal,
    BOOL Permit
    )
{

    TCHAR   keyName [256];
    DWORD   err;

    wcscpy(keyName, TEXT("APPID\\"));
    wcscat(keyName, AppID);

    if (SetPrincipal)
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
                                               keyName,
                                               TEXT("LaunchPermission"),
                                               Principal);
        err = AddPrincipalToNamedValueSD (HKEY_CLASSES_ROOT,
                                          keyName,
                                          TEXT("LaunchPermission"),
                                          Principal,
                                          Permit);
    }
    else
    {
        err = RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
                                               keyName,
                                               TEXT("LaunchPermission"),
                                               Principal);
    }

    return err;
}

//
//  Function will open the metabase and check the iusr_ and iwam_ usernames.
//  it will check if the names are still valid and if the passwords are still valid.
//
VOID UpdateAnonymousUser(IMDCOM *pcCom,
                         LPTSTR pszPath,
                         DWORD dwUserMetaId,
                         DWORD dwPasswordMetaId,
                         DWORD dwUserCommentResourceId,
                         DWORD dwUserFullNameResourceId,
                         LPTSTR pszDefaultUserNamePrefix,
                         USERNAME_STRING_TYPE ustSyncName,
                         PASSWORD_STRING_TYPE pstSyncPass,
                         BOOL fPerformPasswordValidate)
{
    int iReturn = FALSE;
    USERNAME_STRING_TYPE ustAnonyName = L"\0";
    PASSWORD_STRING_TYPE pstAnonyPass = L"\0";
    GUFM_RETURN gufmTemp;
    BOOL fRet;
    BOOL fExistence;
    BOOL fDisabled;
    BOOL fUpdateComApplications = FALSE;

    LPTSTR  pstrRightsFor_IUSR[] = 
        {
        L"SeInteractiveLogonRight",
    	L"SeNetworkLogonRight",
        L"SeBatchLogonRight"
    };

    LPTSTR  pstrRightsFor_IWAM[] = 
        {
    	L"SeNetworkLogonRight",
        L"SeBatchLogonRight",
        L"SeAssignPrimaryTokenPrivilege",
        L"SeIncreaseQuotaPrivilege"
    };


/*
    TCHAR szEntry[_MAX_PATH];
    TCHAR szPassword[LM20_PWLEN+1];


    CreatePassword(szPassword);
*/

    //
    // Get the WAM username and password
    //

    gufmTemp = GetUserFromMetabase(pcCom,
                                   pszPath,
                                   dwUserMetaId,
                                   dwPasswordMetaId,
                                   ustAnonyName,
                                   pstAnonyPass);

    //
    // If the metabase path doesn't exist, then
    // service doesn't exist, punt
    // If ID doesn't exist in the metabase, then punt, assume they
    // don't want an anonymous User. We may want to revisit this.
    //
    //

    if ((gufmTemp != GUFM_NO_PATH) && (gufmTemp != GUFM_NO_USER_ID)) {

        BOOL fCreateAccount = FALSE;

        //
        // See if this is our default account. Otherwise do nothing.
        //

        if (_wcsnicmp(pszDefaultUserNamePrefix,
                      ustAnonyName,
                      wcslen(pszDefaultUserNamePrefix)) == 0) {

            // Check if this user actually exists...
            fExistence = DoesUserExist(ustAnonyName,&fDisabled);

            if (fExistence) 
            {
                if (fDisabled)
                {
                    fCreateAccount = FALSE;
                    if (!g_eventLogForAccountRecreation)
                    {
                        CreateEventLogObject ();
                    }
                    
                    if (g_eventLogForAccountRecreation)
                    {
                        CHAR  szAnsiUserName[MAX_PATH];
                        const CHAR  *pszUserNames[1];
                        
                        
                        if (! WideCharToMultiByte(CP_ACP,
                            0,
                            ustAnonyName,
                            -1,
                            szAnsiUserName,
                            MAX_PATH-1,
                            NULL,
                            NULL))
                        {
                            memset (szAnsiUserName,0,sizeof(szAnsiUserName));
                        }
                        
                        pszUserNames[0] = szAnsiUserName;
                        
                        g_eventLogForAccountRecreation->LogEvent(
                            INET_SVC_ACCOUNT_DISABLED,
                            1,
                            pszUserNames,
                            0 );
                    }
                    
                }
                else
                {
                        if (gufmTemp != GUFM_NO_PASSWORD) {
                        DBG_ASSERT(gufmTemp == GUFM_SUCCESS);

                        if (fPerformPasswordValidate)
                        {
                            BOOL fCheckPassword = TRUE;

                            //
                            // Make sure this is the same password as other
                            // instances of this account. If not, set it.
                            //

                            if ((pstSyncPass[0] != (TCHAR)'\0') &&
                                (_wcsicmp(ustSyncName, ustAnonyName) == 0)) {

                                if (wcscmp(pstSyncPass,
                                             pstAnonyPass) != 0) {

                                    //
                                    // Passwords are different.
                                    //

                                    if (WritePasswordToMetabase(pcCom,
                                                                pszPath,
                                                                dwPasswordMetaId,
                                                                pstSyncPass)) {
                                        wcscpy(pstAnonyPass,
                                               pstSyncPass);
                                    }
                                    else {
                                        fCheckPassword = FALSE;
                                    }
                                }
                            }

                            if (fCheckPassword) {
                                if (ValidatePassword(ustAnonyName,
                                                     TEXT(""),
                                                     pstAnonyPass)) 
                                {
                                    // thats a good case account is ok, do nothing there
                                }
                                else 
                                {
                                    // we comment out  DeleteGuestUser because we try to change pswd on that user
                                    // DeleteGuestUser(ustAnonyName);
                                    fCreateAccount = TRUE;
                                }
                            }
                        }

                        //
                        // Set the sync password here
                        //

                        wcscpy(pstSyncPass,
                               pstAnonyPass);

                        wcscpy(ustSyncName,
                               ustAnonyName);

                    }
                }
            }
            else {
                fCreateAccount = TRUE;
            }

            if (fCreateAccount) {

                //
                // The user does not exist, so let's create it.
                // Make sure there's a password first.
                //

                if (gufmTemp == GUFM_NO_PASSWORD) {
#if 0
                    //
                    // If it's not there then subauth should be set
                    // and the password should not be in the metabase.
                    // Also, if we add it in, then it could cause
                    // a synchronization problem in the IUSR password
                    // between W3 and FTP.
                    //

                    CreatePassword(pstAnonyPass);

                    fCreateAccount = WritePasswordToMetabase(pcCom,
                                                             pszPath,
                                                             dwPasswordMetaId,
                                                             pstAnonyPass);
#endif
                }

                if (fCreateAccount) {
                    if (MD_WAM_USER_NAME == dwUserMetaId)
                    {
                        fRet = CreateUserAccount(ustAnonyName,pstAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,TRUE);
                        if( fRet )
                        {
                            fUpdateComApplications = TRUE;
                        }
                        // if this is a domain controller.
                        // we have to wait for the domain controller
                        // replication to be finished, otherwise The CreateUserAccount
                        // call will fail
                        //
                        // if we failed to create the user
                        // it could be because this is a DC and we need
                        // to wait for the sysvol to be ready.
                        if (!fRet)
                        {
                            if (TRUE == WaitForDCAvailability())
                            {
                                // try again...
                                fRet = CreateUserAccount(ustAnonyName,pstAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,TRUE);
                                if( fRet )
                                {
                                    fUpdateComApplications = TRUE;
                                }
                            }
                        }
                    }
                    else
                    {
                        fRet = CreateUserAccount(ustAnonyName,pstAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,FALSE);
                        if (!fRet)
                        {
                            if (TRUE == WaitForDCAvailability())
                            {
                                // try again...
                                fRet = CreateUserAccount(ustAnonyName,pstAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,FALSE);
                            }
                        }
                    }


                    if (!g_eventLogForAccountRecreation)
                    {
                        CreateEventLogObject ();
                    }

                    if (g_eventLogForAccountRecreation)
                    {
                        CHAR  szAnsiUserName[MAX_PATH];
                        const CHAR  *pszUserNames[1];

                        if (! WideCharToMultiByte(CP_ACP,
                                                      0,
                                                      ustAnonyName,
                                                      -1,
                                                      szAnsiUserName,
                                                      MAX_PATH-1,
                                                      NULL,
                                                      NULL))
                        {
                            memset (szAnsiUserName,0,sizeof(szAnsiUserName));
                        }

                        pszUserNames[0] = szAnsiUserName;

                        // if succeded to recreate an account then log an event
                        if (fRet)
                        {
                            g_eventLogForAccountRecreation->LogEvent(
                                                INET_SVC_ACCOUNT_RECREATED,
                                                1,
                                                pszUserNames,
                                                0 );
                        }
                        else
                        {
                            // if the creation of the account failed, then log that too.
                            g_eventLogForAccountRecreation->LogEvent(
                                                INET_SVC_ACCOUNT_CREATE_FAILED,
                                                1,
                                                pszUserNames,
                                                0 );
                        }
                    }

                    if (dwUserMetaId == MD_WAM_USER_NAME) {
                        ChangeAppIDLaunchACL(TEXT("{9209B1A6-964A-11D0-9372-00A0C9034910}"),
                                             ustAnonyName,
                                             TRUE,
                                             TRUE);
                        ChangeAppIDAccessACL(TEXT("{9209B1A6-964A-11D0-9372-00A0C9034910}"),
                                             ustAnonyName,
                                             TRUE,
                                             TRUE);
                    }
                } // fCreateAccount == TRUE
            } // fCreateAccount == TRUE

            //
            // check if user has enough rights   otherwise add some (bug 361833)
            //

            if (wcscmp(pszDefaultUserNamePrefix,TEXT("IUSR_")) == 0) 
            {
                UpdateUserRights (ustAnonyName,pstrRightsFor_IUSR,sizeof(pstrRightsFor_IUSR)/sizeof(LPTSTR));
            }
            else
            if (wcscmp(pszDefaultUserNamePrefix,TEXT("IWAM_")) == 0) 
            {
                UpdateUserRights (ustAnonyName,pstrRightsFor_IWAM,sizeof(pstrRightsFor_IWAM)/sizeof(LPTSTR));
            }


            // Update the com applications with the new wam user information
            if( fUpdateComApplications )
            {
                HRESULT hr =
                    UpdateComApplications( pcCom, ustAnonyName, pstAnonyPass );

                if( hr != S_OK )
                {
                    if( !g_eventLogForAccountRecreation )
                    {
                        CreateEventLogObject();
                    }

                    if ( g_eventLogForAccountRecreation )
                    {
                        g_eventLogForAccountRecreation->LogEvent(
                            INET_SVC_ACCOUNT_COMUPDATE_FAILED,
                            0,
                            NULL,
                            hr
                            );
                    }
                }
            }
        }
        else
        {
            // This is not one of our accouts.
            // in other words -- it doesn't start with
            // iusr_ or iwam_
            //
            // however there is a problem here.
            //
            // on machines that are made to be replica domain controllers or
            // backup domain controllers, when dcpromo is run to create those types
            // of machines, all the local accounts are wiped out.
            //
            // this is fine if the usernames are iusr_ or iwam_, since they are just
            // re-created in the above code (or the user is warned that they were unable
            // to be crated).  however in the case where these are
            // user created accounts, the user has no way of knowing that 
            // they're iusr/iwam accounts have been hosed.
            //
            // the code here is just to warn the user of that fact.
            if (TRUE == IsDomainController())
            {
                // check if they are valid.
                // Check if this user actually exists...
                fExistence = DoesUserExist(ustAnonyName,&fDisabled);
                if (!fExistence) 
                {
                    if (!fDisabled)
                    {
                        // the user doesn't exist
                        // log SOMETHING at least
                        if (!g_eventLogForAccountRecreation)
                        {
                            CreateEventLogObject ();
                        }

                        if (g_eventLogForAccountRecreation)
                        {
                            CHAR  szAnsiUserName[MAX_PATH];
                            const CHAR  *pszUserNames[1];
                                
                            if (! WideCharToMultiByte(CP_ACP,
                                0,
                                ustAnonyName,
                                -1,
                                szAnsiUserName,
                                MAX_PATH-1,
                                NULL,
                                NULL))
                            {
                                memset (szAnsiUserName,0,sizeof(szAnsiUserName));
                            }
            
                            pszUserNames[0] = szAnsiUserName;

                            g_eventLogForAccountRecreation->LogEvent(
                                                INET_SVC_ACCOUNT_NOT_EXIST,
                                                1,
                                                pszUserNames,
                                                0 );

                        }
                    }
                }
            }
        }
    }
}


HRESULT CreateGroup(LPWSTR szGroupName, LPWSTR szGroupComment)
{
    HRESULT           hr = S_OK;
    NET_API_STATUS    dwRes;
    LOCALGROUP_INFO_1 MyLocalGroup;

    MyLocalGroup.lgrpi1_name    = szGroupName;
    MyLocalGroup.lgrpi1_comment = szGroupComment;

    dwRes = NetLocalGroupAdd(NULL, 1, (LPBYTE)&MyLocalGroup, NULL);
    if(dwRes != NERR_Success &&
       dwRes != NERR_GroupExists &&
       dwRes != ERROR_ALIAS_EXISTS)
    {
        hr = HRESULT_FROM_WIN32(dwRes);
    }

    return hr;
}



VOID 
UpdateUsers(
    BOOL fRestore /* = FALSE */ 
    )
{
    HRESULT hresTemp;
    IMDCOM *pcCom;
    BOOL    fPerformUpdate = TRUE;
    BOOL    fPerformPasswordValidate = FALSE;
    HKEY    hkRegistryKey = NULL;
    DWORD   dwRegReturn,dwBuffer, dwSize, dwType;
    HRESULT hr;

    //
    // First get the metabase interface
    //
    
    
    dwRegReturn = RegOpenKey(HKEY_LOCAL_MACHINE,
                    L"SOFTWARE\\Microsoft\\InetStp",
                     &hkRegistryKey);
    if (dwRegReturn == ERROR_SUCCESS) 
    {
        dwSize = sizeof(dwBuffer);

        dwRegReturn = RegQueryValueEx(hkRegistryKey,
                        L"DisableUserAccountRestore",
                        NULL,
                        &dwType,
                        (BYTE *)&dwBuffer,
                        &dwSize);
        if ((dwRegReturn == ERROR_SUCCESS) && dwType == (REG_DWORD)) 
        {
            fPerformUpdate = FALSE;
        }

        if (fPerformUpdate)
        {
            // we are doing the check to see if the user exists...
            // see if we need to verify that the password is ssynced as well...
            dwRegReturn = RegQueryValueEx(hkRegistryKey,
                            L"EnableUserAccountRestorePassSync",
                            NULL,
                            &dwType,
                            (BYTE *)&dwBuffer,
                            &dwSize);
            if ((dwRegReturn == ERROR_SUCCESS) && dwType == (REG_DWORD)) 
            {
                fPerformPasswordValidate = TRUE;
            }
        }

        RegCloseKey( hkRegistryKey );
    }


    if( fRestore )
    {
        fPerformPasswordValidate = TRUE;    
    }

    if (fPerformUpdate)
    {
        hresTemp = CoCreateInstance(CLSID_MDCOM,
                                    NULL,
                                    CLSCTX_SERVER,
                                    IID_IMDCOM,
                                    (void**) &pcCom);

        if (SUCCEEDED(hresTemp)) {

            //
            // Make sure the IIS_WPG group exists
            //
            hr = CreateGroup(IIS_WP_GROUP, L"IIS Worker Process Group");

            DBGPRINTF((DBG_CONTEXT, "Called into CreateGroup, hr %x\n", hr));

            RegisterAccountToLocalGroup(L"NT Authority\\Local Service",
                                        IIS_WP_GROUP,
                                        TRUE);

            RegisterAccountToLocalGroup(L"NT Authority\\Network Service",
                                        IIS_WP_GROUP,
                                        TRUE);

            PASSWORD_STRING_TYPE pstAnonyPass;
            USERNAME_STRING_TYPE ustAnonyName;

            pstAnonyPass[0] = (TCHAR)'\0';
            ustAnonyName[0] = (TCHAR)'\0';

            UpdateAnonymousUser(pcCom,
                                TEXT("LM/W3SVC"),
                                MD_WAM_USER_NAME,
                                MD_WAM_PWD,
                                IDS_WAMUSER_COMMENT,
                                IDS_WAMUSER_FULLNAME,
                                TEXT("IWAM_"),
                                ustAnonyName,
                                pstAnonyPass,
                                fPerformPasswordValidate);

            DBGPRINTF((DBG_CONTEXT, "Called into Updating IWAM user, hr %x\n", hr));

            pstAnonyPass[0] = (TCHAR)'\0';
            ustAnonyName[0] = (TCHAR)'\0';
            UpdateAnonymousUser(pcCom,
                                TEXT("LM/W3SVC"),
                                MD_ANONYMOUS_USER_NAME,
                                MD_ANONYMOUS_PWD,
                                IDS_USER_COMMENT,
                                IDS_USER_FULLNAME,
                                TEXT("IUSR_"),
                                ustAnonyName,
                                pstAnonyPass,
                                fPerformPasswordValidate);

            DBGPRINTF((DBG_CONTEXT, "Called into Updating IUSR user, hr %x\n", hr));

            //
            // At this point pstAnonyPass should contain the web server password.
            //

            UpdateAnonymousUser(pcCom,
                                TEXT("LM/MSFTPSVC"),
                                MD_ANONYMOUS_USER_NAME,
                                MD_ANONYMOUS_PWD,
                                IDS_USER_COMMENT,
                                IDS_USER_FULLNAME,
                                TEXT("IUSR_"),
                                ustAnonyName,
                                pstAnonyPass,
                                fPerformPasswordValidate);

            DBGPRINTF((DBG_CONTEXT, "Called into Updating IUSR user, hr %x\n", hr));

            hr = UpdateAdminAcl(pcCom,
                                L"/LM/W3SVC",
                                IIS_WP_GROUP);

            DBGPRINTF((DBG_CONTEXT, "Called into UpdateAdminAcl, hr %x\n", hr));

            pcCom->Release();
        }
    }

    if (g_eventLogForAccountRecreation)
    {
        delete g_eventLogForAccountRecreation;
        g_eventLogForAccountRecreation = NULL;
    }
}

void DumpAdminACL(PSECURITY_DESCRIPTOR pSD)
{
    BOOL b= FALSE, bDaclPresent = FALSE, bDaclDefaulted = FALSE;;
    PACL pDacl = NULL;
    ACCESS_ALLOWED_ACE* pAce;
    ACCESS_MASK dwOldMask, dwNewMask,  dwExtraMask, dwMask;

    DBGPRINTF((DBG_CONTEXT, "Dumping AdminAcl %p\n", pSD));

    b = GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pDacl, &bDaclDefaulted);
    if (b) 
    {
        DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:ACE count: %d\n", (int)pDacl->AceCount));

        // get dacl length  
        DWORD cbDacl = pDacl->AclSize;
        // now check if SID's ACE is there  
        for (int i = 0; i < pDacl->AceCount; i++)  
        {
            if (!GetAce(pDacl, i, (LPVOID *) &pAce))
            {
                DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:GetAce failed with 0x%x\n", GetLastError()));
                continue;
            }

            if (IsValidSid(   (PSID) &(pAce->SidStart)   ) )
            {
                LPTSTR pszSid;

                LPCTSTR ServerName = NULL; // local machine
                DWORD cbName = UNLEN+1;
                TCHAR ReferencedDomainName[200];
                DWORD cbReferencedDomainName = sizeof(ReferencedDomainName);
                SID_NAME_USE sidNameUse = SidTypeUser;
                TCHAR szUserName[UNLEN + 1];

                // dump out the sid in string format
                if (ConvertSidToStringSid(  (PSID) &(pAce->SidStart)  , &pszSid))
                {
                    wcscpy(szUserName, L"(unknown...)");
                    if (LookupAccountSid(ServerName, (PSID) &(pAce->SidStart), szUserName, &cbName, ReferencedDomainName, &cbReferencedDomainName, &sidNameUse))
                    {
                        // echo to logfile
                        DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:Sid[%i]=%S,%S,0x%x,0x%x,0x%x,0x%x\n",i,
                                pszSid,
                                szUserName,
                                pAce->Header.AceType,
                                pAce->Header.AceFlags,
                                pAce->Header.AceSize,
                                pAce->Mask
                                ));
                    }
                    else
                    {
                        DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:Sid[%i]=%S='%S'\n",i,pszSid,szUserName));
                    }

                    
                    LocalFree(LocalHandle(pszSid));
                }
            }
            else
            {
                DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:IsValidSid failed with 0x%x\n", GetLastError()));
            }
        }
    }

    return;
}

BOOL
MakeAbsoluteCopyFromRelative(
    PSECURITY_DESCRIPTOR  psdOriginal,
    PSECURITY_DESCRIPTOR* ppsdNew
    )
{
    // we have to find out whether the original is already self-relative
    SECURITY_DESCRIPTOR_CONTROL         sdc = 0;
    PSECURITY_DESCRIPTOR                psdAbsoluteCopy = NULL;
    DWORD                               dwRevision = 0;
    DWORD                               cb = 0;
    PACL Dacl = NULL, Sacl = NULL;
    PSID Owner = NULL, Group = NULL;
    DWORD                               dwDaclSize = 0;
    DWORD                               dwSaclSize = 0;
    DWORD                               dwOwnerSize = 0;
    DWORD                               dwPrimaryGroupSize = 0;

    if( !IsValidSecurityDescriptor( psdOriginal ) ) {
        goto cleanup;
    }

    if( !GetSecurityDescriptorControl( psdOriginal, &sdc, &dwRevision ) ) {
        DWORD err = GetLastError();
        goto cleanup;
    }

    if( sdc & SE_SELF_RELATIVE ) {
        // the original is in self-relative format, build an absolute copy

        // get required buffer size
        cb = 0;
        MakeAbsoluteSD(
                      psdOriginal,              // address of self-relative SD
                      psdAbsoluteCopy,          // address of absolute SD
                      &cb,                      // address of size of absolute SD
                      NULL,                     // address of discretionary ACL
                      &dwDaclSize,              // address of size of discretionary ACL
                      NULL,                     // address of system ACL
                      &dwSaclSize,              // address of size of system ACL
                      NULL,                     // address of owner SID
                      &dwOwnerSize,             // address of size of owner SID
                      NULL,                     // address of primary-group SID
                      &dwPrimaryGroupSize       // address of size of group SID
                      );

        // alloc the memory
        psdAbsoluteCopy = (PSECURITY_DESCRIPTOR) malloc( cb );
        Dacl = (PACL) malloc( dwDaclSize );
        Sacl = (PACL) malloc( dwSaclSize );
        Owner = (PSID) malloc( dwOwnerSize );
        Group = (PSID) malloc( dwPrimaryGroupSize );

        if(NULL == psdAbsoluteCopy ||
           NULL == Dacl ||
           NULL == Sacl ||
           NULL == Owner ||
           NULL == Group
          ) {
            goto cleanup;
        }

        // make the copy
        if( !MakeAbsoluteSD(
                 psdOriginal,            // address of self-relative SD
                 psdAbsoluteCopy,        // address of absolute SD
                 &cb,                    // address of size of absolute SD
                 Dacl,                  // address of discretionary ACL
                 &dwDaclSize,            // address of size of discretionary ACL
                 Sacl,                  // address of system ACL
                 &dwSaclSize,            // address of size of system ACL
                 Owner,                 // address of owner SID
                 &dwOwnerSize,           // address of size of owner SID
                 Group,          // address of primary-group SID
                 &dwPrimaryGroupSize     // address of size of group SID
                 )
          ) {
            goto cleanup;
        }
    } else {
        // the original is in absolute format, fail
        goto cleanup;
    }

    // paranoia check
    if( !IsValidSecurityDescriptor( psdAbsoluteCopy ) ) {
        goto cleanup;
    }
    if( !IsValidSecurityDescriptor( psdOriginal ) ) {
        goto cleanup;
    }

    *ppsdNew = psdAbsoluteCopy;

    return(TRUE);

cleanup:
    if( Dacl != NULL ) {
        free((PVOID) Dacl );
        Dacl = NULL;
    }
    if( Sacl != NULL ) {
        free((PVOID) Sacl );
        Sacl = NULL;
    }
    if( Owner != NULL ) {
        free((PVOID) Owner );
        Owner = NULL;
    }
    if( Group != NULL ) {
        free((PVOID) Group );
        Group = NULL;
    }
    if( psdAbsoluteCopy != NULL ) {
        free((PVOID) psdAbsoluteCopy );
        psdAbsoluteCopy = NULL;
    }

    *ppsdNew = NULL;

    return (FALSE);
}

BOOL
AddUserAccessToSD(
    IN  PSECURITY_DESCRIPTOR pSd,
    IN  PSID  pSid,
    IN  DWORD NewAccess,
    IN  UCHAR TheAceType,
    OUT PSECURITY_DESCRIPTOR *ppSdNew
    )
{
    ULONG i;
    BOOL bReturn = FALSE;
    BOOL Result;
    BOOL DaclPresent;
    BOOL DaclDefaulted;
    DWORD Length;
    DWORD NewAclLength;
    ACCESS_ALLOWED_ACE* OldAce;
    PACE_HEADER NewAce;
    ACL_SIZE_INFORMATION AclInfo;
    PACL Dacl = NULL;
    PACL NewDacl = NULL;
    PACL NewAceDacl = NULL;
    PSECURITY_DESCRIPTOR NewSD = NULL;
    PSECURITY_DESCRIPTOR OldSD = NULL;
    PSECURITY_DESCRIPTOR outpSD = NULL;
    DWORD cboutpSD = 0;
    BOOL fAceForGroupPresent = FALSE;
    DWORD dwMask;

    OldSD = pSd;

    // only do if the ace is allowed/denied
    if (ACCESS_ALLOWED_ACE_TYPE != TheAceType && ACCESS_DENIED_ACE_TYPE != TheAceType)
    {
        goto Exit;
    }

    // Convert SecurityDescriptor to absolute format. It generates
    // a new SecurityDescriptor for its output which we must free.
    if ( !MakeAbsoluteCopyFromRelative(OldSD, &NewSD) ) 
    {
        goto Exit;
    }

    // Must get DACL pointer from new (absolute) SD
    if(!GetSecurityDescriptorDacl(NewSD,&DaclPresent,&Dacl,&DaclDefaulted)) 
    {
        goto Exit;
    }

    // If no DACL, no need to add the user since no DACL
    // means all accesss
    if( !DaclPresent ) 
    {
        bReturn = TRUE;
        goto Exit;
    }

    // Code can return DaclPresent, but a NULL which means
    // a NULL Dacl is present. This allows all access to the object.
    if( Dacl == NULL )
    {
        bReturn = TRUE;
        goto Exit;
    }

    // Get the current ACL's size
    if( !GetAclInformation(Dacl,&AclInfo,sizeof(AclInfo),AclSizeInformation) ) 
    {
        goto Exit;
    }

    // Check if access is already there
    // --------------------------------
    // Check to see if this SID already exists in there
    // if it does (and it has the right access we want) then forget it, we don't have to do anything more.
    for (i = 0; i < AclInfo.AceCount; i++)  
    {
        ACE_HEADER *pAceHeader;
        ACCESS_ALLOWED_ACE* pAce = NULL;

        if (!GetAce(Dacl, i, (LPVOID *) &pAce))
        {
            goto Exit;
        }

        pAceHeader = (ACE_HEADER *)pAce;

        // check if group sid is already there
        if (EqualSid((PSID) &(pAce->SidStart), pSid))
        {
            if (pAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
            {
                // If the correct access is present, return success
                if ((pAce->Mask & NewAccess) == NewAccess)
                {
                    bReturn = TRUE;
                    goto Exit;
                }
                else
                {
                    // the ace that exist doesn't have the permissions that we want.
                    // If an ACE for our SID exists, we just need to bump
                    // up the access level instead of creating a new ACE
                    fAceForGroupPresent = TRUE;
                }
            }
            break;  
        }
    }
    
    // If we have to create a new ACE
    // (because our user isn't listed in the existing ACL)
    // then let's Create a new ACL to put the new access allowed ACE on
    // --------------------------------
    if (!fAceForGroupPresent)
    {
        NewAclLength = sizeof(ACL) +
                       sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) +
                       GetLengthSid( pSid );

        NewAceDacl = (PACL) LocalAlloc( LMEM_FIXED, NewAclLength );
        if ( NewAceDacl == NULL ) 
        {
            goto Exit;
        }

        if(!InitializeAcl( NewAceDacl, NewAclLength, ACL_REVISION )) 
        {
            goto Exit;
        }

        if (ACCESS_DENIED_ACE_TYPE == TheAceType)
        {
            Result = AddAccessDeniedAce(NewAceDacl,ACL_REVISION,NewAccess,pSid);
        }
        else 
        {
            Result = AddAccessAllowedAce(NewAceDacl,ACL_REVISION,NewAccess,pSid);
        }
        if( !Result ) 
        {
            goto Exit;
        }
        // Grab the 1st ace from the Newly created Dacl
        if(!GetAce( NewAceDacl, 0, (void **)&NewAce )) 
        {
            goto Exit;
        }

        // add CONTAINER_INHERIT_ACE TO AceFlags
        //NewAce->AceFlags |= CONTAINER_INHERIT_ACE;

        Length = AclInfo.AclBytesInUse + NewAce->AceSize;
    }
    else
    {
        Length = AclInfo.AclBytesInUse;
    }

    // Allocate new DACL
    NewDacl = (PACL) LocalAlloc( LMEM_FIXED, Length );
    if(NewDacl == NULL) 
    {
        goto Exit;
    }
    if(!InitializeAcl( NewDacl, Length, ACL_REVISION )) 
    {
        goto Exit;
    }

    // Insert new ACE at the front of the new DACL
    if (!fAceForGroupPresent)
    {
        if(!AddAce( NewDacl, ACL_REVISION, 0, NewAce, NewAce->AceSize )) 
        {
            goto Exit;
        }
    }

    // ----------------------------------------
    // Read thru the old Dacl and get the ACE's
    // add it to the new Dacl
    // ----------------------------------------
    for ( i = 0; i < AclInfo.AceCount; i++ ) 
    {
        ACE_HEADER *pAceHeader;

        Result = GetAce( Dacl, i, (LPVOID*) &OldAce );
        if( !Result ) 
        {
            goto Exit;
        }

        pAceHeader = (ACE_HEADER *)OldAce;

        // If an ACE for our SID exists, we just need to bump
        // up the access level instead of creating a new ACE
        //
        if (pAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
        {
            dwMask = OldAce->Mask;
            if (fAceForGroupPresent)
            {
                if (EqualSid((PSID) &(OldAce->SidStart), pSid))
                {
                    dwMask = NewAccess | OldAce->Mask;
                }
            }

            // now add ace to new dacl   
            Result = AddAccessAllowedAceEx(NewDacl, ACL_REVISION, OldAce->Header.AceFlags,dwMask,(PSID) &(OldAce->SidStart));
            if( !Result ) 
            {
                goto Exit;
            }
        }
        else
        {
            // copy denied or audit ace.
            if (!AddAce(NewDacl, ACL_REVISION, 0xFFFFFFFF,OldAce, pAceHeader->AceSize ))
            {
                goto Exit;
            }
        }
    }


    // Set new DACL for Security Descriptor
    if(!SetSecurityDescriptorDacl(NewSD,TRUE,NewDacl,FALSE)) 
    {
        goto Exit;
    }

    // The new SD is in absolute format. change it to Relative before we pass it back
    cboutpSD = 0;
    MakeSelfRelativeSD(NewSD, outpSD, &cboutpSD);
    outpSD = (PSECURITY_DESCRIPTOR)GlobalAlloc(GPTR, cboutpSD);
    if ( !outpSD )
    {
        goto Exit;
    }

    if (!MakeSelfRelativeSD(NewSD, outpSD, &cboutpSD))
    {
        goto Exit;
    }

    // The new SD is passed back in relative format,
    *ppSdNew = outpSD;

    bReturn = TRUE;

 Exit:
    if (NewSD){free( NewSD );NewSD = NULL;}
    if (NewDacl){LocalFree( NewDacl );NewDacl = NULL;}
    if (NewAceDacl){LocalFree( NewAceDacl );NewAceDacl = NULL;}
    return bReturn;
}

HRESULT
UpdateAdminAcl(
    IMDCOM *    pcCom,
    LPCWSTR     szPath, 
    LPCWSTR     szAccountName
    )
/*++
Routine Description:

    After DCPromo, need to update the AdminAcl in the metabase
--*/
{
    METADATA_HANDLE      hMetabase = NULL;
    METADATA_RECORD      mdrAdminAcl;
    BUFFER               buffSid;
    BUFFER               buffDomainName;
    PSECURITY_DESCRIPTOR pOldSd = NULL;
    PSECURITY_DESCRIPTOR pNewSd = NULL;
    HRESULT              hr;
    DWORD                dwMDGetDataLen;
    DWORD                cbSid;
    DWORD                cchDomainName;
    SID_NAME_USE         peUse;

    hr = pcCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
                                     szPath,
                                     METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                                     OPEN_TIMEOUT_VALUE,
                                     &hMetabase
                                     );
    if( FAILED(hr) )
    {
        DBGPRINTF((DBG_CONTEXT, "Error opening metabase path %S, hr %x\n", szPath, hr));
        goto cleanup;
    }

    MD_SET_DATA_RECORD_EXT( &mdrAdminAcl,
                            MD_ADMIN_ACL,
                            METADATA_INHERIT | METADATA_SECURE | METADATA_REFERENCE,
                            IIS_MD_UT_SERVER,
                            BINARY_METADATA,
                            0,
                            NULL
                            );

    hr = pcCom->ComMDGetMetaData(hMetabase,
                                 NULL,
                                 &mdrAdminAcl,
                                 &dwMDGetDataLen);

    if (FAILED(hr))
    {
        DBGPRINTF((DBG_CONTEXT, "Error retrieving data, hr %x\n", hr));
        goto cleanup;
    }

    pOldSd = (PSECURITY_DESCRIPTOR)mdrAdminAcl.pbMDData;

    //
    // obtain the logon sid of the user/group
    //
    cbSid = buffSid.QuerySize();
    cchDomainName = buffDomainName.QuerySize() / sizeof(WCHAR);
    while(!LookupAccountName(NULL,
                             szAccountName,
                             buffSid.QueryPtr(),
                             &cbSid,
                             (LPWSTR)buffDomainName.QueryPtr(),
                             &cchDomainName,
                             &peUse))
    {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
        {
            DBGPRINTF((DBG_CONTEXT, "Error retrieving account %S, hr %x\n", szAccountName, hr));
            hr = HRESULT_FROM_WIN32(GetLastError());
            goto cleanup;
        }

        if (!buffSid.Resize(cbSid) ||
            !buffDomainName.Resize(cchDomainName * sizeof(WCHAR)))
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            goto cleanup;
        }
    }

    DWORD AccessMask = (MD_ACR_READ | MD_ACR_WRITE | MD_ACR_RESTRICTED_WRITE | MD_ACR_UNSECURE_PROPS_READ | MD_ACR_ENUM_KEYS | MD_ACR_WRITE_DAC);

    DumpAdminACL(pOldSd);

    if (!AddUserAccessToSD(pOldSd,
                           buffSid.QueryPtr(),
                           AccessMask,
                           ACCESS_ALLOWED_ACE_TYPE,
                           &pNewSd))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        DBGPRINTF((DBG_CONTEXT, "Error adding user to AdminAcl, hr %x\n", hr));
        goto cleanup;
    }

    if (pNewSd)
    {
        DumpAdminACL(pNewSd);

        DWORD dwNewSd = GetSecurityDescriptorLength(pNewSd);

        MD_SET_DATA_RECORD_EXT( &mdrAdminAcl,
                                MD_ADMIN_ACL,
                                METADATA_INHERIT | METADATA_SECURE | METADATA_REFERENCE,
                                IIS_MD_UT_SERVER,
                                BINARY_METADATA,
                                dwNewSd,
                                pNewSd
                                );

        hr = pcCom->ComMDSetMetaData(hMetabase,
                                     NULL,
                                     &mdrAdminAcl);

        if (FAILED(hr))
        {
            DBGPRINTF((DBG_CONTEXT, "Error setting adminacl, hr %x\n", hr));
            goto cleanup;
        }
    }

 cleanup:
    if (pNewSd)
    {
        GlobalFree(pNewSd);
        pNewSd = NULL;
    }

    if (hMetabase)
    {
        // Done with the metabase
        pcCom->ComMDCloseMetaObject(hMetabase);
        hMetabase = NULL;
    }

    return hr;
}

HRESULT 
UpdateComApplications( 
    IMDCOM *    pcCom,
    LPCTSTR     szWamUserName, 
    LPCTSTR     szWamUserPass 
    )
/*++
Routine Description:

    If the IWAM account has been modified, it is necessary to update the
    the out of process com+ applications with the correct account
    information. This routine will collect all the com+ applications and
    reset the activation information.

Arguments:

    pcCom               - metabase object
    szWamUserName       - the new user name
    szWamUserPass       - the new user password

Note:

    This routine is a royal pain in the butt. I take back
    all the good things I may have said about com automation.

Return Value:
    
    HRESULT             - Return value from failed API call
                        - E_OUTOFMEMORY
                        - S_OK - everything worked
                        - S_FALSE - encountered a non-fatal error, unable
                          to reset at least one application.
--*/                       
{
    HRESULT     hr = NOERROR;
    BOOL        fNoErrors = TRUE;

    METADATA_HANDLE     hMetabase = NULL;
    WCHAR *             pwszDataPaths = NULL;
    DWORD               cchDataPaths = 0;
    BOOL                fTryAgain;
    DWORD               cMaxApplications;
    WCHAR *             pwszCurrentPath;
    STACK_BUFFER(       bufMDPath, 64 );
    WCHAR *             pwszMDPath = NULL;
    DWORD               dwMDPathLen;

    SAFEARRAY *     psaApplications = NULL;
    SAFEARRAYBOUND  rgsaBound[1];
    DWORD           cApplications;
    VARIANT         varAppKey;
    LONG            rgIndices[1];

    METADATA_RECORD     mdrAppIsolated;
    METADATA_RECORD     mdrAppPackageId;
    METADATA_RECORD     mdrWamClsid;
    DWORD               dwAppIsolated;
    WCHAR               wszAppPackageId[ 40 ];
    WCHAR               wszWamClsid[ 40 ];
    DWORD               dwMDGetDataLen = 0;

    ICOMAdminCatalog *      pComCatalog = NULL;
    ICatalogCollection *    pComAppCollection = NULL;
    ICatalogObject *        pComApp = NULL;
    BSTR                    bstrAppCollectionName = NULL;
    LONG                    nAppsInCollection;
    LONG                    iCurrentApp;
    LONG                    nChanges;
    BOOL                    fAppCreated = FALSE;

    VARIANT     varOldAppIdentity;
    VARIANT     varNewAppIdentity;
    VARIANT     varNewAppPassword;

    // This is built unicode right now. Since all the com apis I need
    // are unicode only I'm using wide characters here. I should get
    // plenty of compiler errors if _UNICODE isn't defined, but just
    // in case....
    DBG_ASSERT( sizeof(TCHAR) == sizeof(WCHAR) );

    DBGPRINTF(( DBG_CONTEXT,
                "Updating activation identity for out of process apps.\n"
                ));

    // Init variants
    VariantInit( &varAppKey );
    VariantInit( &varOldAppIdentity );
    VariantInit( &varNewAppIdentity );
    VariantInit( &varNewAppPassword );

    //
    // Get the applications to be reset by querying the metabase paths
    //

    hr = pcCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
                                     ROOTMDPath,
                                     METADATA_PERMISSION_READ,
                                     OPEN_TIMEOUT_VALUE,
                                     &hMetabase
                                     );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed to open metabase (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }

    // Get the data paths string

    fTryAgain = TRUE;
    do
    {
        hr = pcCom->ComMDGetMetaDataPaths( hMetabase,
                                           NULL,
                                           MD_APP_PACKAGE_ID,
                                           STRING_METADATA,
                                           cchDataPaths,
                                           pwszDataPaths,
                                           &cchDataPaths 
                                           );

        if( HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr )
        {
            delete[] pwszDataPaths;
            pwszDataPaths = NULL;

            pwszDataPaths = new WCHAR[cchDataPaths];
            if( !pwszDataPaths )
            {
                hr = E_OUTOFMEMORY;
                goto cleanup;
            }
        }
        else
        {
            fTryAgain = FALSE;
        }
    }
    while( fTryAgain );

    //
    // Done with the metabase for now
    //
    pcCom->ComMDCloseMetaObject(hMetabase);
    hMetabase = NULL;

    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed to find metadata (%08x) Data(%d)\n",
                   hr,
                   MD_APP_PACKAGE_ID
                   ));
        goto cleanup;
    }
    else if (pwszDataPaths == NULL)
    {
        //
        // If we found no isolated apps, make the path list an empty multisz
        //
        cchDataPaths = 1;
        pwszDataPaths = new WCHAR[cchDataPaths];
        if( !pwszDataPaths )
        {
            hr = E_OUTOFMEMORY;
            goto cleanup;
        }
        pwszDataPaths[0] = L'\0';
    }

    // Determine the maximum number of applications

    cMaxApplications = 1; // The pooled application

    for( pwszCurrentPath = pwszDataPaths; 
         *pwszCurrentPath != L'\0';
         pwszCurrentPath += wcslen(pwszCurrentPath) + 1
         )
    {
        cMaxApplications++;
    }

    //
    // Build a key array and load the com applications.
    //

    // Create an array to hold the keys

    rgsaBound[0].cElements = cMaxApplications;
    rgsaBound[0].lLbound = 0;

    psaApplications = SafeArrayCreate( VT_VARIANT, 1, rgsaBound );
    if( psaApplications == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    // Set the out of process pool application key
    varAppKey.vt = VT_BSTR;
    varAppKey.bstrVal = SysAllocString( W3_OOP_POOL_PACKAGE_ID );
    if( !varAppKey.bstrVal )
    {
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    rgIndices[0] = 0;
    hr = SafeArrayPutElement( psaApplications, rgIndices, &varAppKey );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed setting an element in a safe array (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }

    // For each of the application paths determine if an out of process
    // application is defined there and set the appropriate key into
    // our array    

    MD_SET_DATA_RECORD_EXT( &mdrAppIsolated,
                            MD_APP_ISOLATED,
                            METADATA_NO_ATTRIBUTES,
                            ALL_METADATA,
                            DWORD_METADATA,
                            sizeof(DWORD),
                            (PBYTE)&dwAppIsolated
                            );

    MD_SET_DATA_RECORD_EXT( &mdrAppPackageId,
                            MD_APP_PACKAGE_ID,
                            METADATA_NO_ATTRIBUTES,
                            ALL_METADATA,
                            STRING_METADATA,
                            sizeof(wszAppPackageId),
                            (PBYTE)wszAppPackageId
                            );

    wszAppPackageId[0] = L'\0';

    MD_SET_DATA_RECORD_EXT( &mdrWamClsid,
                            MD_APP_WAM_CLSID,
                            METADATA_NO_ATTRIBUTES,
                            ALL_METADATA,
                            STRING_METADATA,
                            sizeof(wszWamClsid),
                            (PBYTE)wszWamClsid
                            );

    wszWamClsid[0] = L'\0';

    // Go through each data path and set it into our array if
    // it is an isolated application

    cApplications = 1;  // Actual # of applications - 1 for pool

    for( pwszCurrentPath = pwszDataPaths; 
         *pwszCurrentPath != L'\0';
         pwszCurrentPath += wcslen(pwszCurrentPath) + 1
         )
    {
        if( hMetabase != NULL )
        {            
            pcCom->ComMDCloseMetaObject(hMetabase);
            hMetabase = NULL;
        }

        //
        // 20 is the size of L"/LM/W3SVC"
        //
        if( !bufMDPath.Resize( wcslen( pwszCurrentPath ) * 
                               sizeof( WCHAR ) + 20 ) ) 
        {
            hr = E_OUTOFMEMORY;
            goto cleanup;
        }
        
        pwszMDPath = ( WCHAR* )bufMDPath.QueryPtr();
        
        wcscpy( pwszMDPath, ROOTMDPath );
        wcscat( pwszMDPath, pwszCurrentPath );

        //
        // Get rid of the trailing '/' or '\\'
        //
        dwMDPathLen = wcslen( pwszMDPath );
        if( pwszMDPath[ dwMDPathLen - 1 ] == L'\\' || 
            pwszMDPath[ dwMDPathLen - 1 ] == L'/' )
        {
            pwszMDPath[ dwMDPathLen - 1 ] = L'\0';
        }
        
        hr = pcCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
                                         pwszMDPath,
                                         METADATA_PERMISSION_READ,
                                         OPEN_TIMEOUT_VALUE,
                                         &hMetabase
                                         );
        if( FAILED(hr) )
        {
            DBGERROR(( DBG_CONTEXT, 
                       "Failed to open metabase (%08x)\n",
                       hr
                       ));
            goto cleanup;
        }

        hr = pcCom->ComMDGetMetaData( hMetabase,
                                      NULL,
                                      &mdrAppIsolated,
                                      &dwMDGetDataLen
                                      );
        if( FAILED(hr) )
        {
            DBGERROR(( DBG_CONTEXT, 
                       "Failed to get data from the metabase (%08x)"
                       " Path(%S) Data(%d)\n",
                       hr,
                       pwszMDPath,
                       mdrAppIsolated.dwMDIdentifier
                       ));

            fNoErrors = FALSE;
            continue;
        }

        // Is the application out of process
        if( dwAppIsolated == 1 )
        {
            // Get the application id

            hr = pcCom->ComMDGetMetaData( hMetabase,
                                          NULL,
                                          &mdrAppPackageId,
                                          &dwMDGetDataLen
                                          );
            if( FAILED(hr) )
            {
                DBGERROR(( DBG_CONTEXT, 
                           "Failed to get data from the metabase (%08x)"
                           " Path(%S) Data(%d)\n",
                           hr,
                           pwszMDPath,
                           mdrAppPackageId.dwMDIdentifier
                           ));

                fNoErrors = FALSE;
                continue;
            }

            // Get the wam class id

            hr = pcCom->ComMDGetMetaData( hMetabase,
                                          NULL,
                                          &mdrWamClsid,
                                          &dwMDGetDataLen
                                          );
            if( FAILED(hr) )
            {
                DBGERROR(( DBG_CONTEXT, 
                           "Failed to get data from the metabase (%08x)"
                           " Path(%S) Data(%d)\n",
                           hr,
                           pwszMDPath,
                           mdrWamClsid.dwMDIdentifier
                           ));

                fNoErrors = FALSE;
                continue;
            }

            //
            // Close meta object since we may need to write to it after we 
            // create a new COM+ application.
            //
            pcCom->ComMDCloseMetaObject(hMetabase);
            hMetabase = NULL;
            
            hr = CreateCOMPlusApplication( pwszMDPath,
                                           wszAppPackageId,
                                           wszWamClsid,
                                           &fAppCreated );
            if( FAILED( hr ) )
            {
                fNoErrors = FALSE;
                continue;
            }

            if( fAppCreated )
            {
                //
                // We don't need to fix the password for this 
                // COM+ application
                //
                continue;
            }
            
            // Add the application id to the array

            VariantClear( &varAppKey );
            varAppKey.vt = VT_BSTR;
            varAppKey.bstrVal = SysAllocString( wszAppPackageId );
            if( !varAppKey.bstrVal )
            {
                hr = E_OUTOFMEMORY;
                goto cleanup;
            }

            rgIndices[0]++;
            hr = SafeArrayPutElement( psaApplications, 
                                      rgIndices, 
                                      &varAppKey 
                                      );
            if( FAILED(hr) )
            {
                DBGERROR(( DBG_CONTEXT, 
                           "Failed to set safe array element (%08x)\n",
                           hr
                           ));
                VariantClear( &varAppKey );
                rgIndices[0]--;
                fNoErrors = FALSE;
                continue;
            }
            cApplications++;
        }
    }

    // Shrink the size of the safe-array if necessary
    if( cApplications < cMaxApplications )
    {
        rgsaBound[0].cElements = cApplications;

        hr = SafeArrayRedim( psaApplications, rgsaBound );
        if( FAILED(hr) )
        {
            DBGERROR(( DBG_CONTEXT, 
                       "Failed to redim safe array (%08x)\n",
                       hr
                       ));
            goto cleanup;
        }
    }

    //
    // For each application reset the activation identity
    //

    // Use our key array to get the application collection

    hr = CoCreateInstance( CLSID_COMAdminCatalog,
                           NULL,
                           CLSCTX_SERVER,
                           IID_ICOMAdminCatalog,
                           (void**)&pComCatalog
                           );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed to create COM catalog (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }

    hr = pComCatalog->GetCollection( L"Applications", 
                                     (IDispatch **)&pComAppCollection
                                     );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed to get Applications collection (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }

    hr = pComAppCollection->PopulateByKey( psaApplications );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed to populate Applications collection (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }

    // Iterate over the application collection and update all the
    // applications that use IWAM.

    hr = pComAppCollection->get_Count( &nAppsInCollection );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT, 
                   "Failed to get Applications count (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }

    // Init our new app identity and password.

    varNewAppIdentity.vt = VT_BSTR;
    varNewAppIdentity.bstrVal = SysAllocString( szWamUserName );
    if( !varNewAppIdentity.bstrVal )
    {
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    varNewAppPassword.vt = VT_BSTR;
    varNewAppPassword.bstrVal = SysAllocString( szWamUserPass );
    if( !varNewAppPassword.bstrVal )
    {
        hr = E_OUTOFMEMORY;
        goto cleanup;
    }

    for( iCurrentApp = 0; iCurrentApp < nAppsInCollection; ++iCurrentApp )
    {
        if( pComApp )
        {
            pComApp->Release();
            pComApp = NULL;
        }
        if( varOldAppIdentity.vt != VT_EMPTY )
        {
            VariantClear( &varOldAppIdentity );
        }

        hr = pComAppCollection->get_Item( iCurrentApp, 
                                          (IDispatch **)&pComApp );
        if( FAILED(hr) )
        {
            DBGERROR(( DBG_CONTEXT, 
                       "Failed to get item from Applications collection (%08x)\n",
                       hr
                       ));
            fNoErrors = FALSE;
            continue;
        }

        // If the user has set this to something other than the IWAM_
        // user, then we will respect that and not reset the identiy.

        hr = pComApp->get_Value( L"Identity", &varOldAppIdentity );
        if( FAILED(hr) )
        {
            DBGERROR(( DBG_CONTEXT, 
                       "Failed to get Identify from Application (%08x)\n",
                       hr
                       ));
            fNoErrors = FALSE;
            continue;
        }

        DBG_ASSERT( varOldAppIdentity.vt == VT_BSTR );
        if( varOldAppIdentity.vt == VT_BSTR )
        {
            if( memcmp( L"IWAM_", varOldAppIdentity.bstrVal, 10 ) == 0 )
            {
                hr = pComApp->put_Value( L"Identity", varNewAppIdentity );
                if( FAILED(hr) )
                {
                    DBGERROR(( DBG_CONTEXT, 
                               "Failed to set new Identify (%08x)\n",
                               hr
                               ));
                    fNoErrors = FALSE;
                    continue;
                }

                hr = pComApp->put_Value( L"Password", varNewAppPassword );
                if( FAILED(hr) )
                {
                    DBGERROR(( DBG_CONTEXT, 
                               "Failed to set new Password (%08x)\n",
                               hr
                               ));
                    fNoErrors = FALSE;
                    continue;
                }
            }
            else
            {
                DBGINFO(( DBG_CONTEXT,
                          "Unrecognized application identity (%S)\n",
                          varOldAppIdentity.bstrVal
                          ));
            }
        }
    }

    hr = pComAppCollection->SaveChanges( &nChanges );
    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT,
                   "Failed to save changes (%08x)\n",
                   hr
                   ));
        goto cleanup;
    }
    
cleanup:

    if( hMetabase != NULL )
    {
        pcCom->ComMDCloseMetaObject(hMetabase);
        hMetabase = NULL;
    }

    if( psaApplications != NULL )
    {
        SafeArrayDestroy( psaApplications );
    }

    if( pComCatalog != NULL )
    {
        pComCatalog->Release();
    }

    if( pComAppCollection != NULL )
    {
        pComAppCollection->Release();
    }

    if( pComApp != NULL )
    {
        pComApp->Release();
    }

    if( varAppKey.vt != VT_EMPTY )
    {
        VariantClear( &varAppKey );
    }

    if( varOldAppIdentity.vt != VT_EMPTY )
    {
        VariantClear( &varOldAppIdentity );
    }

    if( varNewAppIdentity.vt != VT_EMPTY )
    {
        VariantClear( &varNewAppIdentity );
    }

    if( varNewAppPassword.vt != VT_EMPTY )
    {
        VariantClear( &varNewAppPassword );
    }

    delete [] pwszDataPaths;

    // return
    if( FAILED(hr) )
    {
        return hr;
    }
    else if( fNoErrors == FALSE )
    {
        return S_FALSE;
    }
    else
    {
        return S_OK;
    }
}

int
IsDomainController(void)
{
    int iReturn = FALSE;

    OSVERSIONINFOEX VerInfo;
    ZeroMemory(&VerInfo, sizeof VerInfo);
    VerInfo.dwOSVersionInfoSize = sizeof VerInfo;
    if (GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&VerInfo)))
    {
        if (VER_NT_DOMAIN_CONTROLLER == VerInfo.wProductType)
        {
            iReturn = TRUE;
        }
    }

    return iReturn;
}

BOOL
WaitForDCAvailability(void)
{
    BOOL    bRetVal = FALSE;
    HANDLE  hEvent;
    DWORD   dwResult;
    DWORD   dwCount = 0, dwMax;
    DWORD   dwMaxWait = 20000; // 20 second max wait
    HKEY    hKeyNetLogonParams;

    if (FALSE == IsDomainController())
    {
        // not a domain controller
        // so we don't have to worry about replication delays...
        return TRUE;
    }

    dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("SYSTEM\\CurrentControlSet\\Services\\netlogon\\parameters"),0,KEY_READ,&hKeyNetLogonParams );
    if ( dwResult == ERROR_SUCCESS ) 
    {
        //
        // value exists
        //
        DWORD   dwSysVolReady = 0,
                dwSize = sizeof( DWORD ),
                dwType = REG_DWORD;

        dwResult = RegQueryValueEx( hKeyNetLogonParams,TEXT("SysVolReady"),0,&dwType,(LPBYTE) &dwSysVolReady,&dwSize );
        if ( dwResult == ERROR_SUCCESS ) 
        {
            //
            // SysVolReady?
            //
            if ( dwSysVolReady == 0 ) 
            {
                HANDLE hEvent;
                //
                // wait for SysVol to become ready
                //
                hEvent = CreateEvent( 0, TRUE, FALSE, TEXT("IISSysVolReadyEvent") );
                if ( hEvent ) 
                {

                    dwResult = RegNotifyChangeKeyValue( hKeyNetLogonParams, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE );

                    if ( dwResult == ERROR_SUCCESS )
                    {
                        const DWORD dwMaxCount = 3;
                        do {
                            //
                            // wait for SysVolReady to change
                            // hEvent is signaled for any changes in hKeyNetLogonParams
                            // not just the SysVolReady value.
                            //
                            WaitForSingleObject (hEvent, dwMaxWait / dwMaxCount );
                            dwResult = RegQueryValueEx( hKeyNetLogonParams,TEXT("SysVolReady"),0,&dwType,(LPBYTE) &dwSysVolReady,&dwSize );
                        } while ( dwSysVolReady == 0 && ++dwCount < dwMaxCount );

                        if ( dwSysVolReady ) 
                        {
                            bRetVal = TRUE;
                        }
                    }
                    CloseHandle( hEvent );
                }
            }
            else
            {
                // sysvol is ready
                bRetVal = TRUE;
            }

        }
        else  
        {
            //
            // value is non-existent, SysVol is assumed to be ready
            //
            if ( dwResult == ERROR_FILE_NOT_FOUND ) 
            {
                bRetVal = TRUE;
            }
        }

        RegCloseKey( hKeyNetLogonParams );
    }
    else
    {
        // error opening regkey, maybe it's not even there
        bRetVal = TRUE;
    }

    return bRetVal;
}