1392 lines
34 KiB
C++
1392 lines
34 KiB
C++
#include "advapi.h"
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <aclapi.h>
|
|
#include <windows.h>
|
|
#include <wincrypt.h>
|
|
#include <sitesids.h>
|
|
#include <malloc.h>
|
|
#include <urlmon.h>
|
|
|
|
RTL_CRITICAL_SECTION SiteSidCacheLock;
|
|
|
|
// internal function declarations
|
|
HRESULT MakeSidFromHash(PSID *ppSid, BYTE *pbBuffer, DWORD cbBuffer);
|
|
void UpdateSiteSidUsage(
|
|
HKEY hCacheKey,
|
|
LPCWSTR lpwszSiteSid,
|
|
LPWSTR lpwszSite,
|
|
UINT cbSiteBuffer);
|
|
void Base32Encode(LPVOID pvData, UINT cbData, LPWSTR pchData);
|
|
|
|
typedef HRESULT (STDAPICALLTYPE CREATESECURITYMANAGER) (
|
|
IServiceProvider *pSP, IInternetSecurityManager **ppSM, DWORD dwReserved);
|
|
|
|
CREATESECURITYMANAGER *pfnCoInternetCreateSecurityManager = CoInternetCreateSecurityManager;
|
|
HMODULE hUrlMon = 0;
|
|
|
|
|
|
|
|
PSID
|
|
APIENTRY
|
|
GetSiteSidFromToken(
|
|
IN HANDLE TokenHandle
|
|
)
|
|
{
|
|
PTOKEN_GROUPS RestrictedSids = NULL;
|
|
ULONG ReturnLength;
|
|
NTSTATUS Status;
|
|
PSID psSiteSid = NULL;
|
|
|
|
|
|
Status = NtQueryInformationToken(
|
|
TokenHandle,
|
|
TokenRestrictedSids,
|
|
NULL,
|
|
0,
|
|
&ReturnLength
|
|
);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
return NULL;
|
|
}
|
|
|
|
RestrictedSids = (PTOKEN_GROUPS) RtlAllocateHeap(RtlProcessHeap(), 0, ReturnLength);
|
|
if (RestrictedSids == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
TokenHandle,
|
|
TokenRestrictedSids,
|
|
RestrictedSids,
|
|
ReturnLength,
|
|
&ReturnLength
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
UINT i;
|
|
SID_IDENTIFIER_AUTHORITY InternetSiteAuthority = SECURITY_INTERNETSITE_AUTHORITY;
|
|
|
|
for (i = 0; i < RestrictedSids->GroupCount; i++) {
|
|
|
|
if (RtlCompareMemory((PVOID) &((SID *) RestrictedSids->Groups[i].Sid)->IdentifierAuthority,
|
|
(PVOID) &InternetSiteAuthority,
|
|
sizeof(SID_IDENTIFIER_AUTHORITY)) == sizeof(SID_IDENTIFIER_AUTHORITY))
|
|
{
|
|
psSiteSid = RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthSid((RestrictedSids->Groups[i]).Sid));
|
|
if (psSiteSid == NULL) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
else {
|
|
RtlCopySid(RtlLengthSid((RestrictedSids->Groups[i]).Sid), psSiteSid, (RestrictedSids->Groups[i]).Sid);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, RestrictedSids);
|
|
return psSiteSid;
|
|
}
|
|
|
|
|
|
HRESULT GetRestrictedSids(
|
|
LPCWSTR pszSite,
|
|
SID_AND_ATTRIBUTES *pSidToRestrict,
|
|
ULONG *pCount)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG i = 0;
|
|
long error;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
*pCount = 0;
|
|
|
|
pSidToRestrict[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED;
|
|
pSidToRestrict[1].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED;
|
|
pSidToRestrict[2].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED;
|
|
|
|
|
|
//Get the site SID.
|
|
pSidToRestrict[0].Sid = GetSiteSidFromUrl(pszSite);
|
|
if(!pSidToRestrict[0].Sid)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
i++;
|
|
|
|
//BUGBUG: Get the zone SID.
|
|
|
|
//Get the restricted SID.
|
|
error = RtlAllocateAndInitializeSid(&NtAuthority,
|
|
1,
|
|
SECURITY_RESTRICTED_CODE_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pSidToRestrict[i].Sid);
|
|
if(!error)
|
|
{
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
*pCount = i;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
APIENTRY
|
|
GetSiteNameFromSid(PSID pSid, LPWSTR *pwsSite)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR wsValueNameBuffer[MAX_MANGLED_SITE];
|
|
LPWSTR wsValueName = wsValueNameBuffer;
|
|
HKEY hCacheKey;
|
|
DWORD dwDisposition;
|
|
WCHAR *wszValue;
|
|
ULONG ulValueSize;
|
|
DWORD error;
|
|
|
|
*pwsSite = NULL;
|
|
|
|
error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SITE_SID_CACHE_REG_KEY,
|
|
0, NULL, 0, KEY_ALL_ACCESS,
|
|
NULL, &hCacheKey, &dwDisposition);
|
|
|
|
if (ERROR_SUCCESS != error)
|
|
{
|
|
// Can't write, try read-only. We won't be able to update the cache,
|
|
// but we might be able to return the site name
|
|
|
|
error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SITE_SID_CACHE_REG_KEY,
|
|
0, NULL, 0, KEY_QUERY_VALUE,
|
|
NULL, &hCacheKey, &dwDisposition);
|
|
|
|
if (ERROR_SUCCESS != error) {
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
}
|
|
|
|
GetMangledSiteSid(pSid, MAX_MANGLED_SITE, &wsValueName);
|
|
ASSERT(wsValueName == wsValueNameBuffer);
|
|
|
|
//Get the size and allocate memory for the site name.
|
|
error = RegQueryValueExW(
|
|
hCacheKey,
|
|
wsValueName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ulValueSize);
|
|
|
|
if (ERROR_SUCCESS != error) {
|
|
RegCloseKey(hCacheKey);
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
|
|
wszValue = (WCHAR *) LocalAlloc(0, ulValueSize);
|
|
|
|
if (wszValue != NULL)
|
|
{
|
|
error = RegQueryValueExW(
|
|
hCacheKey,
|
|
wsValueName,
|
|
NULL,
|
|
NULL,
|
|
(BYTE *) wszValue,
|
|
&ulValueSize);
|
|
|
|
if (ERROR_SUCCESS == error)
|
|
{
|
|
*pwsSite = wszValue;
|
|
|
|
UpdateSiteSidUsage(hCacheKey, wsValueName, wszValue, ulValueSize);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(error);
|
|
LocalFree(wszValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
RegCloseKey(hCacheKey);
|
|
|
|
return hr;
|
|
}
|
|
|
|
PSID APIENTRY
|
|
GetSiteSidFromUrl(LPCWSTR wsUrl)
|
|
{
|
|
HRESULT hr;
|
|
PSID pSid = NULL;
|
|
IInternetSecurityManager *pIScManager;
|
|
DWORD cbSecurityId = lstrlenW(wsUrl) * sizeof(WCHAR) + sizeof(DWORD);
|
|
BYTE *pbSecurityId = (BYTE *) alloca(cbSecurityId);
|
|
|
|
HCRYPTPROV hProv;
|
|
HCRYPTHASH hHash;
|
|
|
|
DWORD dwCount;
|
|
DWORD dwHashLen;
|
|
|
|
BYTE *pbBuffer;
|
|
|
|
HKEY hCacheKey;
|
|
DWORD dwDisposition;
|
|
|
|
WCHAR wsValueNameBuffer[MAX_MANGLED_SITE];
|
|
LPWSTR wsValueName = wsValueNameBuffer;
|
|
|
|
ULONG ulValueSize = (lstrlenW(wsUrl) + 1) * sizeof(WCHAR);
|
|
WCHAR *wsValue;
|
|
NTSTATUS Status;
|
|
DWORD error;
|
|
|
|
//Crack the URL to get the site name.
|
|
hr = (*pfnCoInternetCreateSecurityManager)(NULL, &pIScManager, 0);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pIScManager->GetSecurityId(wsUrl, pbSecurityId, &cbSecurityId, 0);
|
|
|
|
if(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
|
|
{
|
|
pbSecurityId = (BYTE *) alloca(cbSecurityId);
|
|
hr = pIScManager->GetSecurityId(wsUrl, pbSecurityId, &cbSecurityId, 0);
|
|
}
|
|
|
|
//Remove the dwZone from the end of the pbSecurityId.
|
|
cbSecurityId -= sizeof(DWORD);
|
|
|
|
pIScManager->Release();
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
SetLastError(hr);
|
|
return NULL;
|
|
}
|
|
|
|
// acquire security context - if this is unsuccessful,
|
|
// the hashing functions cannot be called
|
|
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
|
return NULL;
|
|
|
|
if (!CryptCreateHash(hProv, SITE_SID_HASH_ALGORITHM, 0, 0, &hHash))
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
return NULL;
|
|
}
|
|
|
|
// hash the site name
|
|
if (!CryptHashData(hHash, pbSecurityId, cbSecurityId, 0))
|
|
{
|
|
CryptDestroyHash(hHash);
|
|
CryptReleaseContext(hProv, 0);
|
|
return NULL;
|
|
}
|
|
// TODO: salt? - with local machine name?
|
|
|
|
// get size of the hash value - for memory allocation
|
|
dwCount = sizeof(DWORD);
|
|
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *) &dwHashLen, &dwCount, 0))
|
|
{
|
|
CryptDestroyHash(hHash);
|
|
CryptReleaseContext(hProv, 0);
|
|
return NULL;
|
|
}
|
|
|
|
pbBuffer = (BYTE *) LocalAlloc(0, dwHashLen);
|
|
if (pbBuffer == NULL)
|
|
{
|
|
//out of memory
|
|
CryptDestroyHash(hHash);
|
|
CryptReleaseContext(hProv, 0);
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
if (!CryptGetHashParam(hHash, HP_HASHVAL, pbBuffer, &dwHashLen, 0))
|
|
{
|
|
LocalFree(pbBuffer);
|
|
CryptDestroyHash(hHash);
|
|
CryptReleaseContext(hProv, 0);
|
|
return NULL;
|
|
}
|
|
|
|
CryptDestroyHash(hHash);
|
|
CryptReleaseContext(hProv, 0);
|
|
|
|
// make SID from the hash value in pbBuffer
|
|
hr = MakeSidFromHash(&pSid, pbBuffer, dwHashLen);
|
|
if (FAILED(hr))
|
|
{
|
|
LocalFree(pbBuffer);
|
|
SetLastError(hr);
|
|
return NULL;
|
|
}
|
|
|
|
// check if the SID is already in the SID cache
|
|
// if it is, update the time of last use
|
|
// if not, insert it into the cache, deleting the LRU
|
|
// item if the cache is already full
|
|
|
|
// whatever happens here, we already have the mapping
|
|
// from URL to SID that we wanted, so we always return
|
|
// success
|
|
|
|
//
|
|
// Convert the cracked site name to Unicode
|
|
//
|
|
|
|
__try
|
|
{
|
|
wsUrl = (WCHAR *) alloca(ulValueSize);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return pSid; // alloca failed
|
|
}
|
|
|
|
Status = RtlMultiByteToUnicodeN((WCHAR *) wsUrl, ulValueSize, &dwCount, (char *) pbSecurityId, cbSecurityId);
|
|
if (!(NT_SUCCESS(Status)))
|
|
{
|
|
return pSid;
|
|
}
|
|
((WCHAR *)wsUrl)[dwCount / sizeof(WCHAR)] = L'\0';
|
|
|
|
hCacheKey = NULL;
|
|
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, SITE_SID_CACHE_REG_KEY, 0, NULL, 0,
|
|
KEY_ALL_ACCESS, NULL, &hCacheKey, &dwDisposition) != ERROR_SUCCESS)
|
|
{
|
|
// cannot open/create the cache registry key
|
|
return pSid;
|
|
}
|
|
|
|
GetMangledSiteSid(pSid, MAX_MANGLED_SITE, &wsValueName);
|
|
ASSERT(wsValueName == wsValueNameBuffer);
|
|
|
|
__try
|
|
{
|
|
wsValue = (WCHAR *) alloca(ulValueSize);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
RegCloseKey(hCacheKey); // alloca failed
|
|
return pSid;
|
|
}
|
|
|
|
error = RegQueryValueExW(
|
|
hCacheKey,
|
|
wsValueName,
|
|
NULL,
|
|
NULL,
|
|
(BYTE *) wsValue,
|
|
&ulValueSize);
|
|
|
|
if (ERROR_SUCCESS == error) {
|
|
// this SID is already there in the cache
|
|
// check for collision
|
|
|
|
// if wsValue is "", we already know of a collision
|
|
if (wsValue[0] != L'\0') {
|
|
if (wcscmp(wsUrl, wsValue) != 0) {
|
|
// COLLISION !!!
|
|
|
|
// we handle collision by retaining the SID in
|
|
// the cache and setting the site name to ""
|
|
// so the wrong site name cannot be returned
|
|
|
|
DbgPrint("Site SID Collision:\n\t%ws\n\t%ws\n", wsUrl,wsValue);
|
|
|
|
WCHAR wcNull = L'\0';
|
|
RegSetValueExW(hCacheKey, wsValueName, 0, REG_BINARY,
|
|
(CONST BYTE *) &wcNull, sizeof(WCHAR));
|
|
}
|
|
else {
|
|
UpdateSiteSidUsage(
|
|
hCacheKey,
|
|
wsValueName,
|
|
wsValue,
|
|
ulValueSize);
|
|
}
|
|
}
|
|
}
|
|
else if (ERROR_FILE_NOT_FOUND == error) {
|
|
UpdateSiteSidUsage(hCacheKey, wsValueName, (WCHAR *) wsUrl, 0);
|
|
}
|
|
|
|
RegCloseKey(hCacheKey);
|
|
|
|
return pSid;
|
|
}
|
|
|
|
void UpdateSiteSidUsage(
|
|
HKEY hCacheKey,
|
|
LPCWSTR lpwszSiteSid,
|
|
LPWSTR lpwszSite,
|
|
UINT cbSiteBuffer)
|
|
{
|
|
UINT cbSite = wcslen(lpwszSite)*sizeof(WCHAR) + sizeof(L'\0');
|
|
UINT cbRequiredBuffer = cbSite + sizeof(LARGE_INTEGER);
|
|
BOOL bCheckOverflow = (0 == cbSiteBuffer);
|
|
|
|
LARGE_INTEGER Now;
|
|
|
|
if (cbSiteBuffer < cbRequiredBuffer)
|
|
{
|
|
// Either this is a new cache entry or somebody mucked with the
|
|
// cache data
|
|
LPWSTR lpwsz;
|
|
|
|
__try
|
|
{
|
|
lpwsz = (LPWSTR) alloca(cbRequiredBuffer);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return; // alloca failed
|
|
}
|
|
|
|
wcscpy(lpwsz, lpwszSite);
|
|
cbSiteBuffer = cbRequiredBuffer;
|
|
lpwszSite = lpwsz;
|
|
}
|
|
|
|
// Tack the current time after the end of the string and write it out
|
|
|
|
NtQuerySystemTime(&Now);
|
|
* (UNALIGNED LARGE_INTEGER *) (((BYTE *) lpwszSite) + cbSite) = Now;
|
|
|
|
RegSetValueExW(
|
|
hCacheKey,
|
|
lpwszSiteSid,
|
|
NULL,
|
|
REG_BINARY,
|
|
(BYTE *) lpwszSite,
|
|
cbSiteBuffer);
|
|
|
|
if (!bCheckOverflow)
|
|
return;
|
|
|
|
// Check for cache overflow
|
|
|
|
#define _PTIME(i) ((__int64 *) (pEntryInfo + i * cbEntryInfo))
|
|
#define _PVALUE(i) ((WCHAR *) (_PTIME(i) + 1))
|
|
|
|
static volatile LONG lFlushing = 0;
|
|
|
|
DWORD error;
|
|
DWORD cEntries;
|
|
DWORD cTotalEntries;
|
|
DWORD cbEntryValue;
|
|
DWORD cbMaxEntryValue;
|
|
BYTE *pEntryValue;
|
|
DWORD cbMaxEntryName;
|
|
DWORD cbEntryInfo;
|
|
BYTE *pEntryInfo;
|
|
UINT iFreeEntry;
|
|
UINT iNewestEntry;
|
|
UINT i;
|
|
DWORD cb;
|
|
|
|
error = RegQueryInfoKeyW(hCacheKey, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&cEntries, &cbMaxEntryName, &cbMaxEntryValue,
|
|
NULL, NULL);
|
|
|
|
if (ERROR_SUCCESS != error
|
|
|| cEntries < SITE_SID_CACHE_SIZE_HIGH
|
|
|| 1 == InterlockedExchange((LONG *) &lFlushing, 1))
|
|
{
|
|
return; // Cache is under limit or we are already flushing
|
|
}
|
|
|
|
cbMaxEntryName = (cbMaxEntryName + 1) * sizeof(WCHAR);
|
|
cTotalEntries = cEntries - SITE_SID_CACHE_SIZE_LOW + 1;
|
|
cbEntryInfo = sizeof(LARGE_INTEGER) + cbMaxEntryName;
|
|
|
|
// Allocate space for the largest value size
|
|
|
|
__try
|
|
{
|
|
pEntryValue = (BYTE *) alloca(cbMaxEntryValue);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Allocate space to hold the cache entries to delete (+1)
|
|
|
|
do
|
|
{
|
|
pEntryInfo = (BYTE *) LocalAlloc(0, cbEntryInfo * cTotalEntries);
|
|
|
|
if (NULL == pEntryInfo)
|
|
{
|
|
cTotalEntries = cTotalEntries / 2;
|
|
|
|
if (cTotalEntries < 2)
|
|
{
|
|
lFlushing = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
while (NULL == pEntryInfo);
|
|
|
|
iFreeEntry = 0;
|
|
iNewestEntry = 0;
|
|
i = 0;
|
|
cb = cbEntryInfo - sizeof(LARGE_INTEGER);
|
|
cEntries = 1;
|
|
BOOL bNewNewest = FALSE;
|
|
cbEntryValue = cbMaxEntryValue;
|
|
|
|
while (ERROR_SUCCESS == RegEnumValueW(
|
|
hCacheKey,
|
|
i,
|
|
_PVALUE(iFreeEntry),
|
|
&cb,
|
|
NULL,
|
|
NULL,
|
|
pEntryValue,
|
|
&cbEntryValue))
|
|
{
|
|
++i;
|
|
cb = cbEntryInfo - sizeof(LARGE_INTEGER);
|
|
cbEntryValue = cbMaxEntryValue;
|
|
|
|
* _PTIME(iFreeEntry) = * (__int64 *) (wcschr((WCHAR *) pEntryValue, L'\0') + 1);
|
|
|
|
if (cEntries < cTotalEntries)
|
|
{
|
|
++cEntries;
|
|
++iFreeEntry;
|
|
if (cEntries == cTotalEntries)
|
|
bNewNewest = TRUE;
|
|
}
|
|
else if (*_PTIME(iFreeEntry) < *_PTIME(iNewestEntry))
|
|
{
|
|
UINT t = iNewestEntry;
|
|
iNewestEntry = iFreeEntry;
|
|
iFreeEntry = t;
|
|
bNewNewest = TRUE;
|
|
}
|
|
|
|
if (bNewNewest)
|
|
{
|
|
bNewNewest = FALSE;
|
|
|
|
for (DWORD j = 0; j < cEntries; j++)
|
|
{
|
|
if (j != iFreeEntry)
|
|
if (*_PTIME(j) > *_PTIME(iNewestEntry))
|
|
iNewestEntry = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i >= SITE_SID_CACHE_SIZE_HIGH)
|
|
{
|
|
for (DWORD j = 0; j < cEntries; j++)
|
|
{
|
|
if (j != iFreeEntry)
|
|
RegDeleteValueW(hCacheKey, _PVALUE(j));
|
|
}
|
|
}
|
|
|
|
LocalFree(pEntryInfo);
|
|
|
|
lFlushing = 0;
|
|
|
|
#undef _PVALUE
|
|
#undef _PTIME
|
|
}
|
|
|
|
|
|
|
|
HRESULT MakeSidFromHash(PSID *ppSid, BYTE *pbBuffer, DWORD cbBuffer)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
long error;
|
|
DWORD aSubAuthorities[8];
|
|
SID_IDENTIFIER_AUTHORITY siaAuthority = SITE_SID_CACHE_AUTHORITY;
|
|
|
|
*ppSid = NULL;
|
|
|
|
if(cbBuffer > sizeof(aSubAuthorities))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
memset(aSubAuthorities, 0, sizeof(aSubAuthorities));
|
|
memcpy(aSubAuthorities, pbBuffer, cbBuffer);
|
|
|
|
error = RtlAllocateAndInitializeSid(&siaAuthority,
|
|
(BYTE) ((cbBuffer + 3) / sizeof(DWORD)),
|
|
aSubAuthorities[0],
|
|
aSubAuthorities[1],
|
|
aSubAuthorities[2],
|
|
aSubAuthorities[3],
|
|
aSubAuthorities[4],
|
|
aSubAuthorities[5],
|
|
aSubAuthorities[6],
|
|
aSubAuthorities[7],
|
|
ppSid);
|
|
if(error)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(error);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
APIENTRY
|
|
GetMangledSiteSid(PSID pSid, ULONG cchMangledSite, LPWSTR *ppwszMangledSite)
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY InternetSiteAuthority = SECURITY_INTERNETSITE_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY *InAuthority;
|
|
|
|
InAuthority = RtlIdentifierAuthoritySid(pSid);
|
|
|
|
if (NULL == InAuthority)
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_SID);
|
|
|
|
if (0 != memcmp(
|
|
InAuthority,
|
|
&InternetSiteAuthority,
|
|
sizeof(InternetSiteAuthority)))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_SID);
|
|
}
|
|
|
|
if (cchMangledSite < MAX_MANGLED_SITE)
|
|
{
|
|
*ppwszMangledSite = (WCHAR *) LocalAlloc(
|
|
0,
|
|
MAX_MANGLED_SITE * sizeof(WCHAR));
|
|
if (NULL == *ppwszMangledSite)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// The value of MAX_MANGLED_SITE assumes 4 dwords
|
|
ASSERT(4 == *RtlSubAuthorityCountSid(pSid));
|
|
|
|
Base32Encode(
|
|
RtlSubAuthoritySid(pSid, 0),
|
|
*RtlSubAuthorityCountSid(pSid) * sizeof(DWORD),
|
|
*ppwszMangledSite);
|
|
|
|
// The output string should always be MAX_MANGLED_SITE - 1 chars long
|
|
ASSERT(MAX_MANGLED_SITE - 1 == lstrlenW(*ppwszMangledSite));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
ULONG
|
|
APIENTRY
|
|
GetSiteDirectoryA(
|
|
HANDLE hToken,
|
|
LPSTR lpBuffer,
|
|
ULONG nBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to GetSiteDirectoryW
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING Unicode;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
DWORD ReturnValue;
|
|
#if defined (FE_SB) // GetSiteDirectoryA(); local variable
|
|
ULONG cbAnsiString;
|
|
#endif // FE_SB
|
|
|
|
if ( nBufferLength > MAXUSHORT ) {
|
|
nBufferLength = MAXUSHORT-2;
|
|
}
|
|
|
|
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
|
Unicode->Length = (USHORT)GetSiteDirectoryW(
|
|
hToken,
|
|
Unicode->Buffer,
|
|
Unicode->MaximumLength
|
|
);
|
|
|
|
#if defined (FE_SB) // GetSiteDirectoryA(): bug fix
|
|
//
|
|
// Unicode->Length contains the byte count of unicode string.
|
|
// Original code does "UnicodeLength / sizeof(WCHAR)" to
|
|
// get the size of corresponding ansi string.
|
|
// This is correct in SBCS environment. However in DBCS
|
|
// environment, it's definitely WRONG.
|
|
//
|
|
Status = RtlUnicodeToMultiByteSize( &cbAnsiString, Unicode->Buffer, Unicode->Length );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
if ( nBufferLength > (DWORD)(cbAnsiString ) ) {
|
|
AnsiString.Buffer = lpBuffer;
|
|
AnsiString.MaximumLength = (USHORT)(nBufferLength+1);
|
|
Status = BasepUnicodeStringTo8BitString(&AnsiString,Unicode,FALSE);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
ReturnValue = AnsiString.Length;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// current spec says the length doesn't
|
|
// include null terminate character.
|
|
// this may be a bug but I would like
|
|
// to make this same as US (see US original code).
|
|
//
|
|
ReturnValue = cbAnsiString + 1;
|
|
}
|
|
}
|
|
#else
|
|
if ( nBufferLength > (DWORD)(Unicode->Length>>1) ) {
|
|
AnsiString.Buffer = lpBuffer;
|
|
AnsiString.MaximumLength = (USHORT)(nBufferLength+1);
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiString,Unicode,FALSE);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue = 0;
|
|
}
|
|
else {
|
|
ReturnValue = AnsiString.Length;
|
|
}
|
|
}
|
|
else {
|
|
ReturnValue = ((Unicode->Length)>>1)+1;
|
|
}
|
|
#endif // FE_SB
|
|
return ReturnValue;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetUserSid
|
|
*
|
|
* Allocs space for the user sid, fills it in and returns a pointer. Caller
|
|
* The sid should be freed by calling DeleteUserSid.
|
|
*
|
|
* Note the sid returned is the user's real sid, not the per-logon sid.
|
|
*
|
|
* Returns pointer to sid or NULL on failure.
|
|
*
|
|
* History:
|
|
* 26-Aug-92 Davidc Created.
|
|
\***************************************************************************/
|
|
PSID GetUserSid (HANDLE UserToken)
|
|
{
|
|
PTOKEN_USER pUser;
|
|
PSID pSid;
|
|
DWORD BytesRequired = 200;
|
|
NTSTATUS status;
|
|
|
|
|
|
//
|
|
// Allocate space for the user info
|
|
//
|
|
|
|
pUser = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, BytesRequired);
|
|
|
|
|
|
if (pUser == NULL) {
|
|
// DebugMsg((DM_WARNING, TEXT("GetUserSid: Failed to allocate %d bytes"),
|
|
// BytesRequired));
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Read in the UserInfo
|
|
//
|
|
|
|
status = NtQueryInformationToken(
|
|
UserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
BytesRequired, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
if (status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
//
|
|
// Allocate a bigger buffer and try again.
|
|
//
|
|
|
|
HLOCAL realloc = LocalReAlloc(pUser, BytesRequired, LMEM_MOVEABLE);
|
|
if (NULL == realloc) {
|
|
// DebugMsg((DM_WARNING, TEXT("GetUserSid: Failed to allocate %d bytes"),
|
|
// BytesRequired));
|
|
LocalFree(pUser);
|
|
return NULL;
|
|
}
|
|
pUser = (PTOKEN_USER) realloc;
|
|
|
|
status = NtQueryInformationToken(
|
|
UserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
BytesRequired, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
// DebugMsg((DM_WARNING, TEXT("GetUserSid: Failed to query user info from user token, status = 0x%x"),
|
|
// status));
|
|
LocalFree(pUser);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BytesRequired = RtlLengthSid(pUser->User.Sid);
|
|
pSid = LocalAlloc(LMEM_FIXED, BytesRequired);
|
|
if (pSid == NULL) {
|
|
// DebugMsg((DM_WARNING, TEXT("GetUserSid: Failed to allocate %d bytes"),
|
|
// BytesRequired));
|
|
LocalFree(pUser);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
status = RtlCopySid(BytesRequired, pSid, pUser->User.Sid);
|
|
|
|
LocalFree(pUser);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
// DebugMsg((DM_WARNING, TEXT("GetUserSid: RtlCopySid Failed. status = %d"),
|
|
// status));
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
|
|
|
|
return pSid;
|
|
}
|
|
|
|
BOOL
|
|
CreateSiteDirectory(
|
|
LPCWSTR pszSiteDirectory,
|
|
PSID psidUser,
|
|
PSID psidSite)
|
|
{
|
|
BOOL bRetVal = FALSE;
|
|
SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
|
|
PACL pAcl = NULL;
|
|
PSID psidSystem = NULL;
|
|
PSID psidAdmin = NULL;
|
|
DWORD cbAcl, AceIndex, dwDisp;
|
|
ACE_HEADER * lpAceHeader;
|
|
SECURITY_DESCRIPTOR sd;
|
|
SECURITY_ATTRIBUTES saSite;
|
|
|
|
|
|
//
|
|
// Get the system sid
|
|
//
|
|
|
|
if (!AllocateAndInitializeSid(&authNT, 1, SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &psidSystem)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the admin sid
|
|
//
|
|
|
|
if (!AllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS, 0, 0,
|
|
0, 0, 0, 0, &psidAdmin)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the ACL
|
|
//
|
|
|
|
cbAcl = (2 * GetLengthSid (psidUser)) + (2 * GetLengthSid (psidSystem)) +
|
|
(2 * GetLengthSid (psidAdmin)) + (2 * GetLengthSid (psidSite)) +
|
|
sizeof(ACL) +
|
|
(8 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));
|
|
|
|
|
|
pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl);
|
|
if (!pAcl) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Add Aces for User, System, and Admin. Non-inheritable ACEs first
|
|
//
|
|
|
|
AceIndex = 0;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidUser)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidSystem)) {
|
|
goto Exit;
|
|
}
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidAdmin)) {
|
|
goto Exit;
|
|
}
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidSite)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Now the inheritable ACEs
|
|
//
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidUser)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!GetAce(pAcl, AceIndex, (void **)&lpAceHeader)) {
|
|
goto Exit;
|
|
}
|
|
|
|
lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
|
|
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidSystem)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!GetAce(pAcl, AceIndex, (void **)&lpAceHeader)) {
|
|
goto Exit;
|
|
}
|
|
|
|
lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
|
|
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidAdmin)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!GetAce(pAcl, AceIndex, (void **)&lpAceHeader)) {
|
|
goto Exit;
|
|
}
|
|
|
|
lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
|
|
|
|
AceIndex++;
|
|
if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidSite)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!GetAce(pAcl, AceIndex, (void **)&lpAceHeader)) {
|
|
goto Exit;
|
|
}
|
|
|
|
lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
|
|
|
|
|
|
//
|
|
// Put together the security descriptor
|
|
//
|
|
|
|
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
if (!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Add the security descriptor to the sa structure
|
|
//
|
|
|
|
saSite.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saSite.lpSecurityDescriptor = &sd;
|
|
saSite.bInheritHandle = FALSE;
|
|
|
|
//
|
|
// Attempt to create the directory
|
|
//
|
|
|
|
bRetVal = CreateDirectoryW(pszSiteDirectory, &saSite);
|
|
|
|
Exit:
|
|
|
|
//
|
|
// Free the sids and acl
|
|
//
|
|
|
|
if (psidSystem) {
|
|
FreeSid(psidSystem);
|
|
}
|
|
|
|
if (psidAdmin) {
|
|
FreeSid(psidAdmin);
|
|
}
|
|
|
|
if (pAcl) {
|
|
GlobalFree (pAcl);
|
|
}
|
|
return bRetVal;
|
|
}
|
|
|
|
ULONG
|
|
APIENTRY
|
|
GetSiteDirectoryW(
|
|
HANDLE hToken,
|
|
LPWSTR pszSiteDirectory,
|
|
ULONG uSize)
|
|
{
|
|
ULONG cb = 0;
|
|
PSID psidUser = 0;
|
|
PSID psidSite = 0;
|
|
WCHAR szProfile[MAX_PATH + 1];
|
|
LPWSTR pszProfile = szProfile;
|
|
HANDLE hProcessToken = 0;
|
|
long error = 0;
|
|
|
|
//Get the process token.
|
|
if(!hToken)
|
|
{
|
|
if(OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
&hProcessToken))
|
|
{
|
|
hToken = hProcessToken;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//Get the path to the user profile directory.
|
|
psidUser = GetUserSid(hToken);
|
|
if(psidUser != NULL)
|
|
{
|
|
UNICODE_STRING wstrUserSid = {0, 0, 0};
|
|
|
|
error = RtlConvertSidToUnicodeString(&wstrUserSid,
|
|
psidUser,
|
|
TRUE);
|
|
if(!error)
|
|
{
|
|
HKEY hkUserProfile;
|
|
|
|
//Read the registry key.
|
|
lstrcpyW(szProfile, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\");
|
|
lstrcatW(szProfile, wstrUserSid.Buffer);
|
|
|
|
error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
szProfile,
|
|
0,
|
|
KEY_READ,
|
|
&hkUserProfile);
|
|
if(!error)
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwSize = sizeof(szProfile);
|
|
|
|
error = RegQueryValueExW(hkUserProfile,
|
|
L"ProfileImagePath",
|
|
NULL,
|
|
&dwType,
|
|
(BYTE*)szProfile,
|
|
&dwSize );
|
|
if(!error)
|
|
{
|
|
if ( dwType == REG_EXPAND_SZ )
|
|
{
|
|
pszProfile = (LPWSTR) alloca((MAX_PATH + 1) * sizeof(WCHAR));
|
|
ExpandEnvironmentStringsW(szProfile,
|
|
pszProfile,
|
|
MAX_PATH);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkUserProfile);
|
|
}
|
|
|
|
RtlFreeUnicodeString(&wstrUserSid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = GetLastError();
|
|
}
|
|
|
|
|
|
//Get the path to the site directory.
|
|
if(!error)
|
|
{
|
|
psidSite = GetSiteSidFromToken(hToken);
|
|
|
|
if(psidSite != NULL)
|
|
{
|
|
WCHAR wszSiteNameBuffer[MAX_MANGLED_SITE];
|
|
LPWSTR wszSiteName = wszSiteNameBuffer;
|
|
|
|
GetMangledSiteSid(psidSite, MAX_MANGLED_SITE, &wszSiteName);
|
|
ASSERT(wszSiteName == wszSiteNameBuffer);
|
|
|
|
cb = sizeof(WCHAR) * (lstrlenW(pszProfile)
|
|
+ sizeof('\\')
|
|
+ lstrlenW(wszSiteName)
|
|
+ sizeof('\0'));
|
|
|
|
if(uSize * 2 < cb)
|
|
{
|
|
return cb/2;
|
|
}
|
|
else
|
|
{
|
|
lstrcpyW(pszSiteDirectory, pszProfile);
|
|
lstrcatW(pszSiteDirectory, L"\\");
|
|
lstrcatW(pszSiteDirectory, wszSiteName);
|
|
|
|
//Check if the directory already exists.
|
|
if(GetFileAttributesW(pszSiteDirectory) == -1)
|
|
{
|
|
CreateSiteDirectory(pszSiteDirectory, psidUser, psidSite);
|
|
}
|
|
}
|
|
RtlFreeSid(psidSite);
|
|
}
|
|
else
|
|
{
|
|
error = GetLastError();
|
|
}
|
|
}
|
|
|
|
if(error)
|
|
{
|
|
SetLastError(error);
|
|
}
|
|
|
|
if(hProcessToken)
|
|
{
|
|
CloseHandle(hProcessToken);
|
|
}
|
|
|
|
if(psidUser)
|
|
{
|
|
RtlFreeSid(psidUser);
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
BOOL APIENTRY
|
|
IsProcessRestricted(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if the current process is a restricted process.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the current process is a restricted process.
|
|
FALSE if it isn't, or if the handle of the current process
|
|
cannot be obtained
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
static long fIsRestricted = -1;
|
|
|
|
if(-1 == fIsRestricted)
|
|
{
|
|
HANDLE hToken;
|
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
|
|
{
|
|
fIsRestricted = IsTokenRestricted(hToken);
|
|
CloseHandle(hToken);
|
|
}
|
|
else
|
|
{
|
|
fIsRestricted = 0;
|
|
}
|
|
}
|
|
return fIsRestricted;
|
|
}
|
|
|
|
WINADVAPI
|
|
BOOL
|
|
WINAPI IsInSandbox(VOID)
|
|
{
|
|
return IsProcessRestricted();
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: CoInternetCreateSecurityManager
|
|
//
|
|
// Synopsis: Loads urlmon.dll and calls CoInternetCreateSecurityManager.
|
|
//
|
|
// Returns: S_OK, ERROR_MOD_NOT_FOUND
|
|
//
|
|
//--------------------------------------------------------------------
|
|
STDAPI CoInternetCreateSecurityManager(
|
|
IN IServiceProvider *pSP,
|
|
OUT IInternetSecurityManager **ppSM,
|
|
IN DWORD dwReserved)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if(!hUrlMon)
|
|
{
|
|
hUrlMon = LoadLibraryA("urlmon.dll");
|
|
}
|
|
|
|
if(hUrlMon != 0)
|
|
{
|
|
void *pfn = GetProcAddress(hUrlMon, "CoInternetCreateSecurityManager");
|
|
if(pfn != NULL)
|
|
{
|
|
pfnCoInternetCreateSecurityManager = (CREATESECURITYMANAGER *) pfn;
|
|
hr = (*pfnCoInternetCreateSecurityManager)(pSP, ppSM, dwReserved);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: Base32Encode
|
|
//
|
|
// Synopsis: Convert the given data to base32
|
|
//
|
|
// Notes: Adapted from Mim64Encode in the mshtml project.
|
|
//
|
|
// For 128 bit input (4 DWORDs) the output string will be
|
|
// 27 chars long (including the null terminator)
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void Base32Encode(LPVOID pvData, UINT cbData, LPWSTR pchData)
|
|
{
|
|
static const WCHAR alphabet[32] =
|
|
{ L'a', L'b', L'c', L'd', L'e', L'f', L'g', L'h',
|
|
L'i', L'j', L'k', L'l', L'm', L'n', L'o', L'p',
|
|
L'q', L'r', L's', L't', L'u', L'v', L'w', L'x',
|
|
L'y', L'z', L'0', L'1', L'2', L'3', L'4', L'5' };
|
|
|
|
int shift = 0; // The # of unprocessed bits in accum
|
|
ULONG accum = 0; // The unprocessed bits
|
|
ULONG value;
|
|
BYTE *pData = (BYTE *) pvData;
|
|
|
|
// For each byte...
|
|
|
|
while (cbData)
|
|
{
|
|
// Move the byte into the low bits of the accumulator
|
|
|
|
accum = (accum << 8) | *pData++;
|
|
shift += 8;
|
|
--cbData;
|
|
|
|
// Lop off the high 5 or 10 bits and write them out
|
|
|
|
while ( shift >= 5 )
|
|
{
|
|
shift -= 5;
|
|
value = (accum >> shift) & 0x1Fl;
|
|
|
|
*pchData++ = alphabet[value];
|
|
}
|
|
}
|
|
|
|
// If there are any remaining bits, push out one more char padded with 0's
|
|
|
|
if (shift)
|
|
{
|
|
value = (accum << (5 - shift)) & 0x1Fl;
|
|
|
|
*pchData++ = alphabet[value];
|
|
}
|
|
|
|
*pchData = L'\0';
|
|
}
|
|
|