text: /*++
  
  Copyright (c) 1996, 1997  Microsoft Corporation
  
  Module Name:
  
      secmisc.c
  
  Abstract:
  
      This module contains miscellaneous security routines for the Protected
      Storage.
  
  
  Author:
  
      Scott Field (sfield)    25-Mar-97
  
  --*/
  
  #include <nt.h>
  #include <ntrtl.h>
  #include <nturtl.h>
  
  #include <windows.h>
  #include "secmisc.h"
  
  #include "unicode5.h"
  #include "debug.h"
  
  BOOL
  GetUserHKEYEx(
      IN      LPCWSTR szUser,
      IN      DWORD   dwDesiredAccess,
      IN  OUT HKEY    *hKeyUser,
      IN      BOOL    fCheckDefault       // check .Default registry hive when user's not available?
      )
  {
      HKEY hKey = NULL;
      LONG lRet;
  
      //
      // first, try HKEY_USERS\szUser
      // note on WinNT, szUser is an textual sid of the form S-1-5-21-xxx ...
      //      on Win95, szUser is the username associated with the logged on user
      //
  
      //
      // unfortunately, if szUser is null or empty, the RegOpenKeyEx below will
      // succeed, which is not correct behavior for us.  Check for this case
      // and retry with default on if fCheckDefault is TRUE.
      //
  
      if(szUser == NULL || szUser[0] == L'\0') {
          // invalid szUser specified.
          lRet = ERROR_FILE_NOT_FOUND;
      } else {
          lRet = RegOpenKeyExU(
                      HKEY_USERS,
                      szUser,
                      0,      // dwOptions
                      dwDesiredAccess,
                      &hKey
                      );
      }
  
      if( lRet != ERROR_SUCCESS && fCheckDefault ) {
  
          //
          // if that failed, try HKEY_USERS\.Default (for services on NT).
          // TODO (lookat), for now, don't fall back to HKEY_USERS\.Default on Win95
          // because that is shared across users when profiles are disabled.
          //
  
          lRet = RegOpenKeyExU(
                      HKEY_USERS,
                      L".Default",
                      0,      // dwOptions
                      dwDesiredAccess,
                      &hKey
                      );
      }
  
      if(lRet != ERROR_SUCCESS) {
          SetLastError( (DWORD)lRet );
          return FALSE;
      }
  
      *hKeyUser = hKey;
  
      return TRUE;
  }
  
  BOOL
  GetUserHKEY(
      IN      LPCWSTR szUser,
      IN      DWORD   dwDesiredAccess,
      IN  OUT HKEY    *hKeyUser
      )
  {
      //
      // winnt: try HKEY_USERS\.Default
      // win95: don't go to HKEY_USERS\.Default when profiles disabled.
      //
  
      BOOL fRet = GetUserHKEYEx(szUser, dwDesiredAccess, hKeyUser, FALSE);
  
      if(!fRet) 
      {
          //
          // see if local system.  If so, retry against .Default
          //
  
          static const WCHAR szTextualSidSystem[] = TEXTUAL_SID_LOCAL_SYSTEM;
  
          if( memcmp(szUser, szTextualSidSystem, sizeof(szTextualSidSystem)) == 0 )
              fRet = GetUserHKEYEx(szUser, dwDesiredAccess, hKeyUser, TRUE);
  
      }
  
      return fRet;
  }
  
  BOOL
  GetUserTextualSid(
      IN HANDLE hUserToken,        // optional
      IN  OUT LPWSTR  lpBuffer,
      IN  OUT LPDWORD nSize
      )
  {
      HANDLE hToken;
      PSID pSidUser = NULL;
      BOOL fSuccess = FALSE;
  
      if(hUserToken == NULL)
      {
          if(!OpenThreadToken(
                      GetCurrentThread(),
                      TOKEN_QUERY,
                      TRUE,
                      &hToken
                      ))
          {
              return FALSE;
          }
      }
      else
      {
          hToken = hUserToken;
      }
  
      fSuccess = GetTokenUserSid(hToken, &pSidUser);
  
      if(fSuccess) 
      {
          //
          // obtain the textual representaion of the Sid
          //
  
          fSuccess = GetTextualSid(
                          pSidUser,   // user binary Sid
                          lpBuffer,   // buffer for TextualSid
                          nSize       // required/result buffer size in chars (including NULL)
                          );
      }
  
      if(pSidUser)
          SSFree(pSidUser);
  
      if(hToken != hUserToken)
      {
          CloseHandle(hToken);
      }
  
      return fSuccess;
  }
  
  BOOL
  GetTextualSid(
      IN      PSID    pSid,          // binary Sid
      IN  OUT LPWSTR  TextualSid,  // buffer for Textual representaion of Sid
      IN  OUT LPDWORD dwBufferLen // required/provided TextualSid buffersize
      )
  {
      PSID_IDENTIFIER_AUTHORITY psia;
      DWORD dwSubAuthorities;
      DWORD dwCounter;
      DWORD dwSidSize;
  
  
      if(!IsValidSid(pSid)) return FALSE;
  
      // obtain SidIdentifierAuthority
      psia = GetSidIdentifierAuthority(pSid);
  
      // obtain sidsubauthority count
      dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
  
      //
      // compute buffer length (conservative guess)
      // S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
      //
      dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(WCHAR);
  
      //
      // check provided buffer length.
      // If not large enough, indicate proper size and setlasterror
      //
      if(*dwBufferLen < dwSidSize) {
          *dwBufferLen = dwSidSize;
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
          return FALSE;
      }
  
      //
      // prepare S-SID_REVISION-
      //
      dwSidSize = wsprintfW(TextualSid, L"S-%lu-", SID_REVISION );
  
      //
      // prepare SidIdentifierAuthority
      //
      if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) ) {
          dwSidSize += wsprintfW(TextualSid + dwSidSize,
                      L"0x%02hx%02hx%02hx%02hx%02hx%02hx",
                      (USHORT)psia->Value[0],
                      (USHORT)psia->Value[1],
                      (USHORT)psia->Value[2],
                      (USHORT)psia->Value[3],
                      (USHORT)psia->Value[4],
                      (USHORT)psia->Value[5]);
      } else {
          dwSidSize += wsprintfW(TextualSid + dwSidSize,
                      L"%lu",
                      (ULONG)(psia->Value[5]      )   +
                      (ULONG)(psia->Value[4] <<  8)   +
                      (ULONG)(psia->Value[3] << 16)   +
                      (ULONG)(psia->Value[2] << 24)   );
      }
  
      //
      // loop through SidSubAuthorities
      //
      for (dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++) {
          dwSidSize += wsprintfW(TextualSid + dwSidSize,
              L"-%lu", *GetSidSubAuthority(pSid, dwCounter) );
      }
  
      *dwBufferLen = dwSidSize + 1; // tell caller how many chars (include NULL)
  
      return TRUE;
  }
  
  BOOL
  GetThreadAuthenticationId(
      IN      HANDLE  hThread,
      IN  OUT PLUID   AuthenticationId
      )
  /*++
  
      This function retrieves the authentication Id (LUID) from the access token
      specified by the calling thread.
  
      The thread specified by hThread must be impersonating a client.
  
  --*/
  {
      HANDLE hToken;
      TOKEN_STATISTICS TokenInfo;
      DWORD dwReturnLen;
      BOOL bSuccess;
  
      if(!OpenThreadToken(
          hThread,
          TOKEN_QUERY,
          TRUE,
          &hToken
          )) return FALSE;
  
      bSuccess = GetTokenInformation(
          hToken,
          TokenStatistics,
          &TokenInfo,
          sizeof(TokenInfo),
          &dwReturnLen
          );
  
      CloseHandle(hToken);
  
      if(!bSuccess) return FALSE;
  
      memcpy(AuthenticationId, &(TokenInfo.AuthenticationId), sizeof(LUID));
      return TRUE;
  }
  
  BOOL
  GetTokenAuthenticationId(
      IN      HANDLE  hUserToken,
      IN  OUT PLUID   AuthenticationId
      )
  /*++
  
      This function retrieves the authentication Id (LUID) from the specified
      access token.
  
  --*/
  {
      TOKEN_STATISTICS TokenInfo;
      DWORD dwReturnLen;
      BOOL bSuccess;
      HANDLE hToken = NULL;
  
      if(hUserToken == NULL)
      {
          if(!OpenThreadToken(
                      GetCurrentThread(),
                      TOKEN_QUERY,
                      TRUE,
                      &hToken
                      ))
          {
              return FALSE;
          }
      }
      else
      {
          hToken = hUserToken;
      }
  
      bSuccess = GetTokenInformation(
          hToken,
          TokenStatistics,
          &TokenInfo,
          sizeof(TokenInfo),
          &dwReturnLen
          );
  
      if(hToken != hUserToken)
      {
          CloseHandle(hToken);
      }
  
      if(!bSuccess) return FALSE;
  
      memcpy(AuthenticationId, &(TokenInfo.AuthenticationId), sizeof(LUID));
      return TRUE;
  }
  
  BOOL
  GetTokenUserSid(
      IN      HANDLE  hUserToken,     // token to query
      IN  OUT PSID    *ppUserSid  // resultant user sid
      )
  /*++
  
      This function queries the access token specified by the
      hToken parameter, and returns an allocated copy of the
      TokenUser information on success.
  
      The access token specified by hToken must be opened for
      TOKEN_QUERY access.
  
      On success, the return value is TRUE.  The caller is
      responsible for freeing the resultant UserSid via a call
      to SSFree().
  
      On failure, the return value is FALSE.  The caller does
      not need to free any buffer.
  
  --*/
  {
      BYTE FastBuffer[256];
      LPBYTE SlowBuffer = NULL;
      PTOKEN_USER ptgUser;
      DWORD cbBuffer;
      BOOL fSuccess = FALSE;
      HANDLE hToken = NULL;
  
      *ppUserSid = NULL;
  
      if(hUserToken == NULL)
      {
          if(!OpenThreadToken(
                      GetCurrentThread(),
                      TOKEN_QUERY,
                      TRUE,
                      &hToken
                      ))
          {
              return FALSE;
          }
      }
      else
      {
          hToken = hUserToken;
      }
  
      //
      // try querying based on a fast stack based buffer first.
      //
  
      ptgUser = (PTOKEN_USER)FastBuffer;
      cbBuffer = sizeof(FastBuffer);
  
      fSuccess = GetTokenInformation(
                      hToken,    // identifies access token
                      TokenUser, // TokenUser info type
                      ptgUser,   // retrieved info buffer
                      cbBuffer,  // size of buffer passed-in
                      &cbBuffer  // required buffer size
                      );
  
      if(!fSuccess) {
  
          if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  
              //
              // try again with the specified buffer size
              //
  
              SlowBuffer = (LPBYTE)SSAlloc(cbBuffer);
  
              if(SlowBuffer != NULL) {
                  ptgUser = (PTOKEN_USER)SlowBuffer;
  
                  fSuccess = GetTokenInformation(
                                  hToken,    // identifies access token
                                  TokenUser, // TokenUser info type
                                  ptgUser,   // retrieved info buffer
                                  cbBuffer,  // size of buffer passed-in
                                  &cbBuffer  // required buffer size
                                  );
              }
          }
      }
  
      //
      // if we got the token info successfully, copy the
      // relevant element for the caller.
      //
  
      if(fSuccess) {
  
          DWORD cbSid;
  
          // reset to assume failure
          fSuccess = FALSE;
  
          cbSid = GetLengthSid(ptgUser->User.Sid);
  
          *ppUserSid = SSAlloc( cbSid );
  
          if(*ppUserSid != NULL) {
              fSuccess = CopySid(cbSid, *ppUserSid, ptgUser->User.Sid);
          }
          else
          {
              fSuccess = FALSE;
          }
      }
  
      if(!fSuccess) {
          if(*ppUserSid) {
              SSFree(*ppUserSid);
              *ppUserSid = NULL;
          }
      }
  
      if(SlowBuffer)
          SSFree(SlowBuffer);
  
      if(hToken != hUserToken)
      {
          CloseHandle(hToken);
      }
  
      return fSuccess;
  }
  
  BOOL
  SetRegistrySecurity(
      IN      HKEY    hKey
      )
  /*++
  
      The function applies security to the specifed registry key such that only
      Local System has Full Control to the registry key.  Note that the owner
      is not set, which results in a default owner of Administrators.
  
      The specified hKey must to opened for WRITE_DAC access.
  
  --*/
  {
      SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
      PSID pLocalSystemSid = NULL;
      SECURITY_DESCRIPTOR sd;
      PACL pDacl = NULL;
      PACCESS_ALLOWED_ACE pAce;
      DWORD dwAclSize;
      LONG lRetCode;
      BOOL bSuccess = FALSE; // assume this function fails
  
      //
      // prepare a Sid representing the Local System account
      //
  
      if(!AllocateAndInitializeSid(
          &sia,
          1,
          SECURITY_LOCAL_SYSTEM_RID,
          0, 0, 0, 0, 0, 0, 0,
          &pLocalSystemSid
          )) goto cleanup;
  
      //
      // compute size of new acl
      //
  
      dwAclSize = sizeof(ACL) +
          1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
          GetLengthSid(pLocalSystemSid) ;
  
      //
      // allocate storage for Acl
      //
  
      pDacl = (PACL)SSAlloc(dwAclSize);
      if(pDacl == NULL) goto cleanup;
  
      if(!InitializeAcl(pDacl, dwAclSize, ACL_REVISION))
          goto cleanup;
  
      if(!AddAccessAllowedAce(
          pDacl,
          ACL_REVISION,
          KEY_ALL_ACCESS,
          pLocalSystemSid
          )) goto cleanup;
  
      //
      // make it container inherit.
      //
  
      if(!GetAce(pDacl, 0, &pAce))
          goto cleanup;
  
      pAce->Header.AceFlags = CONTAINER_INHERIT_ACE;
  
      if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
          goto cleanup;
  
      if(!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) {
          goto cleanup;
      }
  
      //
      // apply the security descriptor to the registry key
      //
  
      lRetCode = RegSetKeySecurity(
          hKey,
          (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
          &sd
          );
  
      if(lRetCode != ERROR_SUCCESS) {
          goto cleanup;
      }
  
      bSuccess = TRUE; // indicate success
  
  cleanup:
  
      //
      // free allocated resources
      //
  
      if(pDacl != NULL)
          SSFree(pDacl);
  
      if(pLocalSystemSid != NULL)
          FreeSid(pLocalSystemSid);
  
      return bSuccess;
  }
  
  BOOL
  SetPrivilege(
      HANDLE hToken,          // token handle
      LPCWSTR Privilege,      // Privilege to enable/disable
      BOOL bEnablePrivilege   // to enable or disable privilege
      )
  {
      TOKEN_PRIVILEGES tp;
      LUID luid;
      TOKEN_PRIVILEGES tpPrevious;
      DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
  
      if(!LookupPrivilegeValueW( NULL, Privilege, &luid )) return FALSE;
  
      //
      // first pass.  get current privilege setting
      //
      tp.PrivilegeCount           = 1;
      tp.Privileges[0].Luid       = luid;
      tp.Privileges[0].Attributes = 0;
  
      AdjustTokenPrivileges(
              hToken,
              FALSE,
              &tp,
              sizeof(TOKEN_PRIVILEGES),
              &tpPrevious,
              &cbPrevious
              );
  
      if (GetLastError() != ERROR_SUCCESS) return FALSE;
  
      //
      // second pass.  set privilege based on previous setting
      //
      tpPrevious.PrivilegeCount       = 1;
      tpPrevious.Privileges[0].Luid   = luid;
  
      if(bEnablePrivilege) {
          tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
      }
      else {
          tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
              tpPrevious.Privileges[0].Attributes);
      }
  
      AdjustTokenPrivileges(
              hToken,
              FALSE,
              &tpPrevious,
              cbPrevious,
              NULL,
              NULL
              );
  
      if (GetLastError() != ERROR_SUCCESS) return FALSE;
  
      return TRUE;
  }
  
  BOOL
  SetCurrentPrivilege(
      LPCWSTR Privilege,      // Privilege to enable/disable
      BOOL bEnablePrivilege   // to enable or disable privilege
      )
  {
      BOOL bSuccess=FALSE; // assume failure
      HANDLE hToken;
  
      if(OpenProcessToken(
              GetCurrentProcess(),
              TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
              &hToken
              ))
      {
  
          if(SetPrivilege(hToken, Privilege, bEnablePrivilege)) bSuccess=TRUE;
  
          CloseHandle(hToken);
      }
  
      return bSuccess;
  }
  
  #if 0
  BOOL
  IsDelegating(
      IN      HANDLE hToken   // token to query, open for at least TOKEN_QUERY access
      )
  /*++
  
      This function determines if the specified access token represents an
      impersonation at the delegation impersonate level.
  
      The access token specified by the hToken parameter must be opened for
      at least TOKEN_QUERY access.
  
      If the return value is TRUE, the specified hToken is impersonating at
      delegation level.
  
      If the return value is FALSE, hToken does not represent delegation level
      impersonation.
  
  --*/
  {
      DWORD dwImpersonationLevel;
      DWORD cbTokenInfo;
  
      if( GetTokenInformation(
                  hToken,
                  TokenImpersonationLevel,
                  &dwImpersonationLevel,
                  sizeof(dwImpersonationLevel),
                  &cbTokenInfo
                  ) && dwImpersonationLevel == SecurityDelegation ) {
  
          return TRUE;
      }
  
      return FALSE;
  }
  #endif
  
  BOOL
  IsUserSidInDomain(
      IN      PSID pSidDomain,    // domain Sid
      IN      PSID pSidUser       // user Sid
      )
  /*++
  
      This function determines if the user associated with the
      pSidUser parameter exists in the domain specified by pSidDomain.
  
      If the user is in the specified domain, the return value is TRUE.
      Otherwise, the return value is FALSE.
  
  --*/
  {
      DWORD dwSubauthorityCount;
      DWORD dwSubauthIndex;
  
      //
      // pickup count of subauthorities in domain sid.  The domain Sid
      // is the prefix associated with user sids, so use that count
      // as the basis for our comparison.
      //
  
      dwSubauthorityCount = (DWORD)*GetSidSubAuthorityCount( pSidDomain );
  
      if( dwSubauthorityCount >= (DWORD)*GetSidSubAuthorityCount( pSidUser ) )
          return FALSE;
  
      //
      // compare identifier authority values.
      //
  
      if(memcmp(  GetSidIdentifierAuthority(pSidDomain),
                  GetSidIdentifierAuthority(pSidUser),
                  sizeof(SID_IDENTIFIER_AUTHORITY)  ) != 0)
          return FALSE;
  
      //
      // loop through subauthorities comparing equality.
      //
  
      for(dwSubauthIndex = 0 ;
          dwSubauthIndex < dwSubauthorityCount ;
          dwSubauthIndex++) {
  
          if( *GetSidSubAuthority(pSidDomain, dwSubauthIndex) !=
              *GetSidSubAuthority(pSidUser, dwSubauthIndex) )
              return FALSE;
      }
  
  
      return TRUE;
  }
  
  #if 0
  BOOL
  IsAdministrator(
      VOID
      )
  /*++
  
      This function determines if the calling user is an Administrator.
  
      On Windows 95, this function always returns TRUE, as there is
      no difference between users on that platform.
  
      On Windows NT, the caller of this function must be impersonating
      the user which is to be queried.  If the caller is not impersonating,
      this function will always return FALSE.
  
  --*/
  {
      HANDLE hAccessToken;
      SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
      PSID psidAdministrators = NULL;
      BOOL bSuccess;
  
      if(!OpenThreadToken(
              GetCurrentThread(),
              TOKEN_QUERY,
              TRUE,
              &hAccessToken
              )) return FALSE;
  
      bSuccess = AllocateAndInitializeSid(
              &siaNtAuthority,
              2,
              SECURITY_BUILTIN_DOMAIN_RID,
              DOMAIN_ALIAS_RID_ADMINS,
              0, 0, 0, 0, 0, 0,
              &psidAdministrators
              );
  
      if( bSuccess ) {
          BOOL fIsMember = FALSE;
  
          bSuccess = CheckTokenMembership( hAccessToken, psidAdministrators, &fIsMember );
  
          if( bSuccess && !fIsMember )
              bSuccess = FALSE;
  
      }
  
      CloseHandle( hAccessToken );
  
      if(psidAdministrators)
          FreeSid(psidAdministrators);
  
      return bSuccess;
  }
  #endif
  
  BOOL
  IsLocal(
      VOID
      )
  /*++
  
      This function determines if the calling user is logged on locally
  
      On Windows NT, the caller of this function must be impersonating
      the user which is to be queried.  If the caller is not impersonating,
      this function will always return FALSE.
  
  --*/
  {
      HANDLE hAccessToken;
      SID_IDENTIFIER_AUTHORITY siaLocalAuthority = SECURITY_LOCAL_SID_AUTHORITY;
      PSID psidLocal = NULL;
      BOOL bSuccess;
  
      if(!OpenThreadToken(
              GetCurrentThread(),
              TOKEN_QUERY,
              TRUE,
              &hAccessToken
              )) return FALSE;
  
      bSuccess = AllocateAndInitializeSid(
              &siaLocalAuthority,
              1,
              SECURITY_LOCAL_RID,
              0, 0, 0, 0, 0, 0, 0,
              &psidLocal
              );
  
      if( bSuccess ) {
          BOOL fIsMember = FALSE;
  
          bSuccess = CheckTokenMembership( hAccessToken, psidLocal, &fIsMember );
  
          if( bSuccess && !fIsMember )
              bSuccess = FALSE;
  
      }
  
      CloseHandle( hAccessToken );
  
      if(psidLocal)
          FreeSid(psidLocal);
  
      return bSuccess;
  }
  
  
  BOOL
  IsDomainController(
      VOID
      )
  /*++
  
      This function returns TRUE if the current machine is a Windows NT
      domain controller.
  
      The function returns FALSE if the current machine is not a Windows NT
      domain controller.
  
  --*/
  {
      HMODULE hNtDll;
  
      typedef BOOLEAN (NTAPI *RTLGETNTPRODUCTTYPE)(
          OUT PNT_PRODUCT_TYPE NtProductType
          );
  
      RTLGETNTPRODUCTTYPE _RtlGetNtProductType;
      NT_PRODUCT_TYPE NtProductType;
  
      hNtDll = GetModuleHandleW(L"ntdll.dll");
      if( hNtDll == NULL )
          return FALSE;
  
  
      _RtlGetNtProductType = (RTLGETNTPRODUCTTYPE)GetProcAddress( hNtDll, "RtlGetNtProductType" );
      if( _RtlGetNtProductType == NULL )
          return FALSE;
  
  
      if(_RtlGetNtProductType( &NtProductType )) {
          if( NtProductType == NtProductLanManNt )
              return TRUE;
      }
  
      return FALSE;
  }
  
  #if 0
  LONG
  SecureRegDeleteValueU(
      IN      HKEY hKey,          // handle of key
      IN      LPCWSTR lpValueName // address of value name
      )
  /*++
  
      This function securely deletes a value from the registry.  This approach
      avoids leaving a copy of the old data in the registry backing file after
      the delete has occurred.
  
      The specified registry handle hKey must be opened for
      REG_QUERY_VALUE | REG_SET_VALUE | DELETE access.
  
      On success, the return value is ERROR_SUCCESS.
      On error, the return value is a Win32 error code.
  
  --*/
  {
      DWORD dwType;
      DWORD cbData;
  
      BYTE FastBuffer[ 256 ];
      LPBYTE lpData;
      LPBYTE SlowBuffer = NULL;
  
      LONG lRet;
  
      cbData = 0; // query size of value data.
  
      //
      // query the current size of the registry data.
      // zero the current registry data of current size.
      // delete the registry data.
      // flush the change to disk.
      //  If errors occur, just do a regular delete.
      //
  
      lRet = RegQueryValueExU(
                  hKey,
                  lpValueName,
                  NULL,
                  &dwType,
                  NULL,
                  &cbData
                  );
  
      if( lRet == ERROR_MORE_DATA ) {
  
          BOOL fSet = TRUE; // assume ok to set
  
          //
          // select fast buffer if large enough.  otherwise, allocate a buffer
          //
  
          if(cbData <= sizeof(FastBuffer)) {
              lpData = FastBuffer;
          } else {
              SlowBuffer = (LPBYTE)SSAlloc( cbData );
  
              if(SlowBuffer == NULL) {
                  fSet = FALSE; // failure.
              } else {
                  lpData = SlowBuffer;
              }
  
          }
  
          if( fSet ) {
  
              ZeroMemory( lpData, cbData );
  
              RegSetValueExU(
                          hKey,
                          lpValueName,
                          0,
                          dwType,
                          lpData,
                          cbData
                          );
          }
      }
  
      lRet = RegDeleteValueU( hKey, lpValueName );
  
      RegFlushKey( hKey );
  
      if( SlowBuffer )
          SSFree( SlowBuffer );
  
      return lRet;
  }
  #endif
  