Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2266 lines
58 KiB

/*++
Copyright (c) 1996-2000 Microsoft Corporation
Module Name:
session.c
Abstract:
This module contains routines to support communication with the LSA
(Local Security Authority) to permit querying of active sessions.
This module also supports calling into the LSA to retrieve a credential
derived from a logged on user specified by Logon Identifier.
Author:
Scott Field (sfield) 02-Mar-97
Pete Skelly (petesk) 09-May-00 - added credential history and signature code
--*/
#include <pch.cpp>
#pragma hdrstop
#include <ntmsv1_0.h>
#include <crypt.h>
#include <userenv.h>
#include <userenvp.h>
#include "debug.h"
#include "passrec.h"
#define HMAC_K_PADSIZE (64)
#define CREDENTIAL_HISTORY_VERSION 1
#define CREDENTIAL_HISTORY_SALT_SIZE 16 // 128 bits
#define DEFAULT_KEY_GEN_ALG CALG_HMAC
#define DEFAULT_ENCRYPTION_ALG CALG_3DES
typedef struct _CREDENTIAL_HISTORY_HEADER
{
DWORD dwVersion;
GUID CredentialID;
DWORD dwPreviousCredOffset;
} CREDENTIAL_HISTORY_HEADER, *PCREDENTIAL_HISTORY_HEADER;
typedef struct _CREDENTIAL_HISTORY
{
CREDENTIAL_HISTORY_HEADER Header;
DWORD dwFlags;
DWORD KeyGenAlg;
DWORD cIterationCount; // pbkdf2 iteration count
DWORD cbSid; // sid is used as mixing bytes
DWORD KeyEncrAlg;
DWORD cbShaOwf;
DWORD cbNtOwf;
BYTE Salt[CREDENTIAL_HISTORY_SALT_SIZE];
} CREDENTIAL_HISTORY, *PCREDENTIAL_HISTORY;
typedef struct _CREDENTIAL_HISTORY_MAP
{
PSID pUserSid;
WCHAR wszFilePath[MAX_PATH+1];
HANDLE hHistoryFile;
HANDLE hMapping;
DWORD dwMapSize;
PBYTE pMapping;
struct _CREDENTIAL_HISTORY_MAP *pNext;
} CREDENTIAL_HISTORY_MAP, *PCREDENTIAL_HISTORY_MAP;
RTL_CRITICAL_SECTION g_csCredHistoryCache;
DWORD
OpenCredentialHistoryMap(
HANDLE hUserToken,
LPWSTR pszProfilePath,
PCREDENTIAL_HISTORY_MAP *ppMap,
PCREDENTIAL_HISTORY *ppCurrent);
PCREDENTIAL_HISTORY
GetPreviousCredentialHistory(
PCREDENTIAL_HISTORY_MAP pMap,
PCREDENTIAL_HISTORY pCurrent
);
DWORD
CloseCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap,
BOOL fReader);
DWORD
DestroyCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap);
VOID
DeriveWithHMAC_SHA1(
IN PBYTE pbKeyMaterial, // input key material
IN DWORD cbKeyMaterial,
IN PBYTE pbData, // input mixing data
IN DWORD cbData,
IN OUT BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer
);
DWORD
DecryptCredentialHistory(PCREDENTIAL_HISTORY pCredential,
BYTE rgbDecryptingCredential[A_SHA_DIGEST_LEN],
BYTE rgbShaOwf[A_SHA_DIGEST_LEN],
BYTE rgbNTOwf[A_SHA_DIGEST_LEN]);
DWORD
RetrieveCurrentDerivedCredential(
IN LUID *pLogonId,
IN BOOL fDPOWF,
IN PBYTE pbMixingBytes,
IN DWORD cbMixingBytes,
IN OUT BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN]
)
{
NTSTATUS ntstatus;
DWORD rc = ERROR_SUCCESS;
NTSTATUS AuthPackageStatus;
PMSV1_0_DERIVECRED_REQUEST pDeriveCredentialRequest;
DWORD cbDeriveCredentialRequest;
PMSV1_0_DERIVECRED_RESPONSE pDeriveCredentialResponse;
ULONG DeriveCredentialResponseLength;
UNICODE_STRING PackageName;
HANDLE hToken = NULL;
RtlInitUnicodeString(&PackageName, L"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0");
//
// must specify mixing bytes.
//
if( cbMixingBytes == 0 || pbMixingBytes == NULL )
{
return ERROR_INVALID_PARAMETER;
}
//
// Ask authentication package to provide derived credential associated
// with the specified logon identifier.
// note: submit buffer must be a single contiguous block.
//
cbDeriveCredentialRequest = sizeof(MSV1_0_DERIVECRED_REQUEST) + cbMixingBytes;
pDeriveCredentialRequest = (MSV1_0_DERIVECRED_REQUEST *)SSAlloc( cbDeriveCredentialRequest );
if( pDeriveCredentialRequest == NULL )
{
return ERROR_INVALID_PARAMETER;
}
pDeriveCredentialRequest->MessageType = MsV1_0DeriveCredential;
CopyMemory( &(pDeriveCredentialRequest->LogonId), pLogonId, sizeof(LUID) );
pDeriveCredentialRequest->DeriveCredType = fDPOWF?MSV1_0_DERIVECRED_TYPE_SHA1_V2:MSV1_0_DERIVECRED_TYPE_SHA1;
pDeriveCredentialRequest->DeriveCredInfoLength = cbMixingBytes;
CopyMemory(pDeriveCredentialRequest->DeriveCredSubmitBuffer, pbMixingBytes, cbMixingBytes);
if(!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken))
{
return GetLastError();
}
RevertToSelf();
// Make this call as local system
ntstatus = LsaICallPackage(
&PackageName,
pDeriveCredentialRequest,
cbDeriveCredentialRequest,
(PVOID *)&pDeriveCredentialResponse,
&DeriveCredentialResponseLength,
&AuthPackageStatus
);
SSFree( pDeriveCredentialRequest );
if (!SetThreadToken(NULL, hToken))
{
rc = GetLastError();
}
CloseHandle(hToken);
if(!NT_SUCCESS(ntstatus))
{
return RtlNtStatusToDosError(ntstatus);
}
if (ERROR_SUCCESS == rc)
{
CopyMemory( rgbDerivedCredential,
pDeriveCredentialResponse->DeriveCredReturnBuffer,
pDeriveCredentialResponse->DeriveCredInfoLength
);
}
RtlSecureZeroMemory( pDeriveCredentialResponse->DeriveCredReturnBuffer,
pDeriveCredentialResponse->DeriveCredInfoLength
);
LsaIFreeReturnBuffer( pDeriveCredentialResponse );
return rc;
}
DWORD
QueryDerivedCredential(
IN OUT GUID *CredentialID,
IN LUID *pLogonId,
IN DWORD dwFlags,
IN PBYTE pbMixingBytes,
IN DWORD cbMixingBytes,
IN OUT BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN]
)
{
DWORD dwLastError = ERROR_SUCCESS;
PCREDENTIAL_HISTORY pCurrent = NULL;
PCREDENTIAL_HISTORY pNext = NULL;
BYTE rgbCurrentDerivedCredential[A_SHA_DIGEST_LEN];
BYTE rgbCurrentShaOWF[A_SHA_DIGEST_LEN];
BYTE rgbCurrentNTOWF[A_SHA_DIGEST_LEN];
PCREDENTIAL_HISTORY_MAP pHistoryMap= NULL;
WCHAR wszTextualSid[MAX_PATH + 1];
WCHAR szProfilePath[MAX_PATH + 1];
DWORD cchTextualSid = 0;
BOOL fIsRoot = TRUE;
//
// Get path to user's profile data. This will typically look something
// like "c:\Documents and Settings\<user>\Application Data".
//
dwLastError = PRGetProfilePath(NULL,
szProfilePath);
if( dwLastError != ERROR_SUCCESS )
{
D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword returned 0x%x\n", dwLastError));
return dwLastError;
}
//
// work-around the fact that much of this (new) code is not thread-safe.
//
RtlEnterCriticalSection(&g_csCredHistoryCache);
//
// Open history file
//
dwLastError = OpenCredentialHistoryMap(NULL, szProfilePath, &pHistoryMap, &pCurrent);
while((ERROR_SUCCESS == dwLastError) &&
(pCurrent) &&
(0 == (dwFlags & USE_ROOT_CREDENTIAL)))
{
//
// We're looking for a specific credential ID
//
if((NULL != CredentialID) &&
(0 == memcmp(&pCurrent->Header.CredentialID, CredentialID, sizeof(GUID))))
{
// found it,
break;
}
pNext = GetPreviousCredentialHistory(pHistoryMap, pCurrent);
if(NULL == pNext)
{
if(NULL != CredentialID)
{
// If we're looking for a specific credential, but
// couldn't find it, then return an error
dwLastError = NTE_BAD_KEY;
}
else
{
// no credential id was specified, so default to the oldest one.
dwLastError = ERROR_SUCCESS;
}
break;
}
//
// get the textual sid
//
cchTextualSid = MAX_PATH;
if(!GetTextualSid((PBYTE)(pNext + 1), wszTextualSid, &cchTextualSid))
{
dwLastError = ERROR_INVALID_PARAMETER;
break;
}
if(fIsRoot)
{
//
// crack the next credential using the current
// credentials
//
dwLastError = RetrieveCurrentDerivedCredential(pLogonId,
(0 != (pNext->dwFlags & USE_DPAPI_OWF)), // always use
(PBYTE)wszTextualSid,
cchTextualSid*sizeof(WCHAR),
rgbCurrentDerivedCredential);
fIsRoot = FALSE;
}
else
{
//
// calculate the current derived credential used to decrypt the
// next credential history structure by using the decrypted OWF
// from the previous pass
DeriveWithHMAC_SHA1((0 != (pNext->dwFlags & USE_DPAPI_OWF))?rgbCurrentShaOWF:rgbCurrentNTOWF,
A_SHA_DIGEST_LEN,
(PBYTE)wszTextualSid,
cchTextualSid*sizeof(WCHAR),
rgbCurrentDerivedCredential);
//
// we don't need the OWF anymore, so zap it.
//
RtlSecureZeroMemory(rgbCurrentShaOWF, A_SHA_DIGEST_LEN);
RtlSecureZeroMemory(rgbCurrentNTOWF, A_SHA_DIGEST_LEN);
}
if(ERROR_SUCCESS != dwLastError)
{
break;
}
//
// used the derived credential to decrypt
// the data blob of pNext
//
dwLastError = DecryptCredentialHistory(pNext,
rgbCurrentDerivedCredential,
rgbCurrentShaOWF,
rgbCurrentNTOWF);
if(ERROR_SUCCESS != dwLastError)
{
break;
}
pCurrent = pNext;
pNext = NULL;
}
if(ERROR_SUCCESS == dwLastError)
{
if(fIsRoot)
{
//
// crack the next credential using the current
// credentials
//
dwLastError = RetrieveCurrentDerivedCredential(pLogonId,
(0 != (dwFlags & USE_DPAPI_OWF)),
pbMixingBytes,
cbMixingBytes,
rgbDerivedCredential);
if(ERROR_SUCCESS == dwLastError)
{
if((CredentialID != NULL) &&
(0 != (dwFlags & USE_ROOT_CREDENTIAL)))
{
CopyMemory(CredentialID, &pCurrent->Header.CredentialID, sizeof(GUID));
}
}
}
else
{
//
// calculate the current derived credential used to decrypt the
// next credential history structure by using the decrypted OWF
// from the previous pass
DeriveWithHMAC_SHA1((0 != (dwFlags & USE_DPAPI_OWF))?rgbCurrentShaOWF:rgbCurrentNTOWF,
A_SHA_DIGEST_LEN,
pbMixingBytes,
cbMixingBytes,
rgbDerivedCredential);
}
}
//
// Clear out any owf's we may have lying around
//
RtlSecureZeroMemory(rgbCurrentShaOWF, A_SHA_DIGEST_LEN);
RtlSecureZeroMemory(rgbCurrentNTOWF, A_SHA_DIGEST_LEN);
if(pHistoryMap)
{
CloseCredentialHistoryMap(pHistoryMap, TRUE);
}
RtlLeaveCriticalSection(&g_csCredHistoryCache);
return dwLastError;
}
VOID
DeriveWithHMAC_SHA1(
IN PBYTE pbKeyMaterial, // input key material
IN DWORD cbKeyMaterial,
IN PBYTE pbData, // input mixing data
IN DWORD cbData,
IN OUT BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer
)
{
unsigned __int64 rgbKipad[ HMAC_K_PADSIZE/sizeof(unsigned __int64) ];
unsigned __int64 rgbKopad[ HMAC_K_PADSIZE/sizeof(unsigned __int64) ];
A_SHA_CTX sSHAHash;
DWORD dwBlock;
// truncate
if( cbKeyMaterial > HMAC_K_PADSIZE )
{
cbKeyMaterial = HMAC_K_PADSIZE;
}
ZeroMemory(rgbKipad, sizeof(rgbKipad));
ZeroMemory(rgbKopad, sizeof(rgbKopad));
CopyMemory(rgbKipad, pbKeyMaterial, cbKeyMaterial);
CopyMemory(rgbKopad, pbKeyMaterial, cbKeyMaterial);
// Kipad, Kopad are padded sMacKey. Now XOR across...
for( dwBlock = 0; dwBlock < (HMAC_K_PADSIZE/sizeof(unsigned __int64)) ; dwBlock++ )
{
rgbKipad[dwBlock] ^= 0x3636363636363636;
rgbKopad[dwBlock] ^= 0x5C5C5C5C5C5C5C5C;
}
// prepend Kipad to data, Hash to get H1
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, (PBYTE)rgbKipad, sizeof(rgbKipad));
A_SHAUpdate(&sSHAHash, pbData, cbData);
// Finish off the hash
A_SHAFinal(&sSHAHash, rgbHMAC);
// prepend Kopad to H1, hash to get HMAC
// note: done in place to avoid buffer copies
// final hash: output value into passed-in buffer
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, (PBYTE)rgbKopad, sizeof(rgbKopad));
A_SHAUpdate(&sSHAHash, rgbHMAC, A_SHA_DIGEST_LEN);
A_SHAFinal(&sSHAHash, rgbHMAC);
RtlSecureZeroMemory( rgbKipad, sizeof(rgbKipad) );
RtlSecureZeroMemory( rgbKopad, sizeof(rgbKopad) );
RtlSecureZeroMemory( &sSHAHash, sizeof(sSHAHash) );
return;
}
DWORD
DecryptCredentialHistory(PCREDENTIAL_HISTORY pCredential,
BYTE rgbDecryptingCredential[A_SHA_DIGEST_LEN],
BYTE rgbShaOwf[A_SHA_DIGEST_LEN],
BYTE rgbNTOwf[A_SHA_DIGEST_LEN])
{
DWORD dwLastError = ERROR_SUCCESS;
DWORD j;
BYTE rgbSymKey[A_SHA_DIGEST_LEN*2]; // big enough to handle 3des keys
//
// Derive the protection key
//
for(j=0; j < 2; j++)
{
if(!PKCS5DervivePBKDF2( rgbDecryptingCredential,
A_SHA_DIGEST_LEN,
pCredential->Salt,
CREDENTIAL_HISTORY_SALT_SIZE,
pCredential->KeyGenAlg,
pCredential->cIterationCount,
j+1,
rgbSymKey + j*A_SHA_DIGEST_LEN))
{
dwLastError = ERROR_INVALID_DATA;
goto cleanup;
}
}
if (CALG_3DES == pCredential->KeyEncrAlg)
{
DES3TABLE s3DESKey;
DWORD iBlock;
BYTE ResultBlock[2*A_SHA_DIGEST_LEN+DES_BLOCKLEN ];
//
// Round up blocks. it's assumed that the total block size was verified
// earlier
//
DWORD cBlocks = (pCredential->cbShaOwf + pCredential->cbNtOwf + DES_BLOCKLEN - 1)/DES_BLOCKLEN;
BYTE feedback[ DES_BLOCKLEN ];
// initialize 3des key
//
if((pCredential->cbShaOwf != A_SHA_DIGEST_LEN) ||
(pCredential->cbNtOwf != A_SHA_DIGEST_LEN))
{
return ERROR_INVALID_PARAMETER;
}
tripledes3key(&s3DESKey, rgbSymKey);
//
// IV is derived from the DES_BLOCKLEN bytes of the calculated
// rgbSymKey, after the 3des key
CopyMemory(feedback, rgbSymKey + DES3_KEYSIZE, DES_BLOCKLEN);
for(iBlock=0; iBlock < cBlocks; iBlock++)
{
CBC(tripledes,
DES_BLOCKLEN,
ResultBlock+iBlock*DES_BLOCKLEN,
((PBYTE)(pCredential + 1) + pCredential->cbSid)+iBlock*DES_BLOCKLEN,
&s3DESKey,
DECRYPT,
feedback);
}
CopyMemory(rgbShaOwf, ResultBlock, A_SHA_DIGEST_LEN);
CopyMemory(rgbNTOwf, ResultBlock + A_SHA_DIGEST_LEN, A_SHA_DIGEST_LEN);
RtlSecureZeroMemory(ResultBlock, sizeof(ResultBlock));
}
else
{
dwLastError = ERROR_INVALID_DATA;
goto cleanup;
}
cleanup:
return dwLastError;
}
DWORD
EncryptCredentialHistory(BYTE rgbEncryptingCredential[A_SHA_DIGEST_LEN],
DWORD dwFlags,
BYTE SHAOwfToEncrypt[A_SHA_DIGEST_LEN],
BYTE NTOwfToEncrypt[A_SHA_DIGEST_LEN],
PCREDENTIAL_HISTORY *ppCredential,
DWORD *pcbCredential)
{
DWORD dwLastError = ERROR_SUCCESS;
DWORD j;
BYTE rgbSymKey[A_SHA_DIGEST_LEN*2]; // big enough to handle 3des keys
PCREDENTIAL_HISTORY pCred = NULL;
DWORD cbCred = 0;
HANDLE hToken = NULL;
PSID pSidUser = NULL;
BYTE ResultBuffer[A_SHA_DIGEST_LEN * 2]; // 2 * A_SHA_DIGEST_LEN
DWORD cBlocks = 0;
DWORD cbBlock = 0;
cbBlock = DES_BLOCKLEN;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
{
dwLastError = GetLastError();
goto error;
}
if(!GetTokenUserSid(hToken, &pSidUser))
{
dwLastError = GetLastError();
goto error;
}
cBlocks = sizeof(ResultBuffer)/DES_BLOCKLEN; // this should be 5
cbCred= sizeof(CREDENTIAL_HISTORY) +
GetLengthSid(pSidUser) +
cBlocks*cbBlock;
pCred = (PCREDENTIAL_HISTORY)LocalAlloc(LMEM_ZEROINIT, cbCred);
if(NULL == pCred)
{
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
goto error;
}
pCred->dwFlags = dwFlags;
pCred->KeyGenAlg = DEFAULT_KEY_GEN_ALG;
pCred->cIterationCount = GetIterationCount();
pCred->KeyEncrAlg = DEFAULT_ENCRYPTION_ALG;
pCred->cbShaOwf = A_SHA_DIGEST_LEN;
pCred->cbNtOwf = A_SHA_DIGEST_LEN;
pCred->cbSid = GetLengthSid(pSidUser);
CopyMemory((PBYTE)(pCred+1), (PBYTE)pSidUser, pCred->cbSid);
if(!RtlGenRandom(pCred->Salt, CREDENTIAL_HISTORY_SALT_SIZE))
{
dwLastError = GetLastError();
goto error;
}
for(j=0; j < 2; j++)
{
if(!PKCS5DervivePBKDF2( rgbEncryptingCredential,
A_SHA_DIGEST_LEN,
pCred->Salt,
CREDENTIAL_HISTORY_SALT_SIZE,
pCred->KeyGenAlg,
pCred->cIterationCount,
j+1,
rgbSymKey + j*A_SHA_DIGEST_LEN))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
}
if (CALG_3DES == pCred->KeyEncrAlg)
{
DES3TABLE s3DESKey;
DWORD iBlock;
BYTE feedback[ DES_BLOCKLEN ];
// initialize 3des key
tripledes3key(&s3DESKey, rgbSymKey);
//
// IV is derived from the DES_BLOCKLEN bytes of the calculated
// rgbSymKey, after the 3des key
CopyMemory(feedback, rgbSymKey + DES3_KEYSIZE, DES_BLOCKLEN);
CopyMemory(ResultBuffer, SHAOwfToEncrypt, A_SHA_DIGEST_LEN);
CopyMemory(ResultBuffer+A_SHA_DIGEST_LEN, NTOwfToEncrypt, A_SHA_DIGEST_LEN);
for(iBlock=0; iBlock < cBlocks; iBlock++)
{
CBC(tripledes,
DES_BLOCKLEN,
((PBYTE)(pCred + 1) + pCred->cbSid)+iBlock*DES_BLOCKLEN,
ResultBuffer + iBlock*DES_BLOCKLEN,
&s3DESKey,
ENCRYPT,
feedback);
}
RtlSecureZeroMemory(ResultBuffer, sizeof(ResultBuffer));
}
else
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
*ppCredential = pCred;
*pcbCredential = cbCred;
pCred = NULL;
error:
if(pCred)
{
LocalFree(pCred);
}
if(hToken)
{
CloseHandle(hToken);
}
if(pSidUser)
{
SSFree(pSidUser);
}
return dwLastError;
}
#define PRODUCT_ROOT_STRING L"\\Microsoft\\Protect\\"
#define HISTORY_FILENAME L"CREDHIST"
DWORD
CreateCredentialHistoryMap(
HANDLE hUserToken,
LPWSTR pszProfilePath,
PCREDENTIAL_HISTORY_MAP *ppMap,
BOOL fRead)
{
PCREDENTIAL_HISTORY_MAP pMap = NULL;
PCREDENTIAL_HISTORY_MAP pCached = NULL;
DWORD dwError = ERROR_SUCCESS;
DWORD dwHighFileSize = 0;
DWORD cbUserStorageRoot;
HANDLE hTemporaryMapping = NULL;
WCHAR szFilePath[MAX_PATH + 1];
PWSTR pszCreationStartPoint;
NTSTATUS Status;
if(NULL == ppMap)
{
return ERROR_INVALID_PARAMETER;
}
pMap = (PCREDENTIAL_HISTORY_MAP)LocalAlloc(LPTR, sizeof(CREDENTIAL_HISTORY_MAP));
if(NULL == pMap)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Obtain the user's SID.
//
if(hUserToken)
{
if(!GetTokenUserSid(hUserToken, &pMap->pUserSid))
{
dwError = GetLastError();
goto error;
}
}
else
{
HANDLE hToken;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
{
dwError = ERROR_NO_TOKEN;
goto error;
}
if(!GetTokenUserSid(hToken, &pMap->pUserSid))
{
dwError = GetLastError();
CloseHandle(hToken);
goto error;
}
CloseHandle(hToken);
}
//
// Open map file
//
if(wcslen(pszProfilePath) + wcslen(PRODUCT_ROOT_STRING) + wcslen(HISTORY_FILENAME) > MAX_PATH)
{
dwError = ERROR_INVALID_PARAMETER;
goto error;
}
wcscpy(szFilePath, pszProfilePath);
// Build the path in a separate buffer just in case we have to create
// the directory.
pszCreationStartPoint = szFilePath + wcslen(szFilePath) + sizeof(WCHAR);
wcscat(szFilePath, PRODUCT_ROOT_STRING);
// Copy the path plus the filename over to the map structure.
wcscpy(pMap->wszFilePath, szFilePath);
wcscat(pMap->wszFilePath, HISTORY_FILENAME);
//
// Create the history file.
//
dwError = ERROR_SUCCESS;
while(TRUE)
{
pMap->hHistoryFile = CreateFileWithRetries(
pMap->wszFilePath,
GENERIC_READ | GENERIC_WRITE,
0, // cannot share this file when open
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM |
FILE_FLAG_RANDOM_ACCESS,
NULL
);
if(INVALID_HANDLE_VALUE == pMap->hHistoryFile)
{
dwError = GetLastError();
if(dwError == ERROR_PATH_NOT_FOUND)
{
// Create the DPAPI directory, and then try to create the file
// again.
Status = DPAPICreateNestedDirectories(szFilePath,
pszCreationStartPoint);
if(!NT_SUCCESS(Status))
{
goto error;
}
dwError = ERROR_SUCCESS;
continue;
}
else
{
goto error;
}
}
break;
}
pMap->dwMapSize = GetFileSize(pMap->hHistoryFile, &dwHighFileSize);
if((-1 == pMap->dwMapSize) ||
(dwHighFileSize != 0))
{
dwError = ERROR_INVALID_PARAMETER;
goto error;
}
//
// If this map is too small, we need to create a new header
//
if(pMap->dwMapSize < sizeof(CREDENTIAL_HISTORY_HEADER))
{
PCREDENTIAL_HISTORY_HEADER pHeader = NULL;
pMap->dwMapSize = sizeof(CREDENTIAL_HISTORY_HEADER);
hTemporaryMapping = CreateFileMapping(pMap->hHistoryFile,
NULL,
PAGE_READWRITE,
0,
pMap->dwMapSize,
NULL);
if(NULL == hTemporaryMapping)
{
dwError = GetLastError();
goto error;
}
pHeader = (PCREDENTIAL_HISTORY_HEADER)(PBYTE)MapViewOfFile(hTemporaryMapping,
FILE_MAP_WRITE,
0,
0,
0);
if(NULL == pHeader)
{
dwError = GetLastError();
goto error;
}
//
// Write a fresh header into the cred history file
//
pHeader->dwPreviousCredOffset = 0;
pHeader->dwVersion = CREDENTIAL_HISTORY_VERSION;
dwError = UuidCreate( &pHeader->CredentialID );
FlushViewOfFile(pHeader, pMap->dwMapSize);
UnmapViewOfFile(pHeader);
if(ERROR_SUCCESS != dwError)
{
goto error;
}
}
*ppMap = pMap;
pMap = NULL;
error:
if(pMap)
{
DestroyCredentialHistoryMap(pMap);
}
if(hTemporaryMapping)
{
CloseHandle(hTemporaryMapping);
}
return dwError;
}
DWORD
OpenCredentialHistoryMap(
HANDLE hUserToken,
LPWSTR pszProfilePath,
PCREDENTIAL_HISTORY_MAP *ppMap,
PCREDENTIAL_HISTORY *ppCurrent)
{
PCREDENTIAL_HISTORY_MAP pMap = NULL;
PCREDENTIAL_HISTORY pCurrent = NULL;
DWORD dwError = ERROR_SUCCESS;
DWORD dwHighFileSize = 0;
WCHAR szFilePath[MAX_PATH+1];
DWORD cbUserStorageRoot;
HANDLE hTemporaryMapping = NULL;
if((NULL == ppMap) ||
(NULL == ppCurrent))
{
return ERROR_INVALID_PARAMETER;
}
dwError = CreateCredentialHistoryMap(
hUserToken,
pszProfilePath,
&pMap,
TRUE);
if(ERROR_SUCCESS != dwError)
{
goto error;
}
if(NULL == pMap->hMapping)
{
//
// Open a read-only mapping of the file
//
pMap->hMapping = CreateFileMapping(pMap->hHistoryFile,
NULL,
PAGE_READONLY,
dwHighFileSize,
pMap->dwMapSize,
NULL);
if(NULL == pMap->hMapping)
{
dwError = GetLastError();
goto error;
}
}
if(NULL == pMap->pMapping)
{
pMap->pMapping = (PBYTE)MapViewOfFile(pMap->hMapping,
FILE_MAP_READ,
0,
0,
0);
if(NULL == pMap->pMapping)
{
dwError = GetLastError();
goto error;
}
}
pCurrent = GetPreviousCredentialHistory(pMap, NULL);
if(NULL == pCurrent)
{
dwError = ERROR_INVALID_PARAMETER;
goto error;
}
*ppMap = pMap;
pMap = NULL;
*ppCurrent = pCurrent;
error:
if(pMap)
{
CloseCredentialHistoryMap(pMap, TRUE);
}
return dwError;
}
PCREDENTIAL_HISTORY
GetPreviousCredentialHistory(
PCREDENTIAL_HISTORY_MAP pMap,
PCREDENTIAL_HISTORY pCurrent
)
{
PCREDENTIAL_HISTORY pPrevious = NULL;
DWORD cbSize = 0;
if((NULL == pMap) ||
(NULL == pMap->pMapping))
{
return NULL;
}
if(pMap->dwMapSize < sizeof(CREDENTIAL_HISTORY_HEADER))
{
return NULL;
}
if(NULL == pCurrent)
{
pPrevious = (PCREDENTIAL_HISTORY)((PBYTE)pMap->pMapping +
pMap->dwMapSize - sizeof(CREDENTIAL_HISTORY_HEADER));
}
else
{
if(((PBYTE)pCurrent < pMap->pMapping) ||
((PBYTE)pCurrent - pMap->pMapping >= (__int64)pMap->dwMapSize) ||
((PBYTE)pCurrent - pMap->pMapping < (__int64)pCurrent->Header.dwPreviousCredOffset))
{
return NULL;
}
pPrevious = (PCREDENTIAL_HISTORY)((PBYTE)pCurrent - pCurrent->Header.dwPreviousCredOffset);
cbSize = sizeof(CREDENTIAL_HISTORY) + pPrevious->cbSid;
if(cbSize > pCurrent->Header.dwPreviousCredOffset)
{
return NULL;
}
cbSize = pCurrent->Header.dwPreviousCredOffset - cbSize;
if(cbSize < pPrevious->cbShaOwf + pPrevious->cbNtOwf)
{
return NULL;
}
if(cbSize % DES_BLOCKLEN)
{
return NULL;
}
if(!IsValidSid((PSID)(pPrevious+1)))
{
return NULL;
}
if(GetLengthSid((PSID)(pPrevious+1)) != pPrevious->cbSid)
{
return NULL;
}
}
// validate found credential history
if(pPrevious->Header.dwVersion != CREDENTIAL_HISTORY_VERSION)
{
return NULL;
}
return pPrevious;
}
DWORD
DestroyCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap)
{
if(NULL == pMap)
{
return ERROR_SUCCESS;
}
if (pMap->pUserSid)
{
SSFree(pMap->pUserSid);
pMap->pUserSid = NULL;
}
if(pMap->pMapping)
{
UnmapViewOfFile(pMap->pMapping);
pMap->pMapping = NULL;
}
if(pMap->hMapping)
{
CloseHandle(pMap->hMapping);
pMap->hMapping = NULL;
}
if(pMap->hHistoryFile)
{
CloseHandle(pMap->hHistoryFile);
pMap->hHistoryFile = NULL;
}
LocalFree(pMap);
return ERROR_SUCCESS;
}
DWORD
CloseCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap, BOOL fReader)
{
if(NULL == pMap)
{
return ERROR_INVALID_PARAMETER;
}
if(pMap->pMapping)
{
UnmapViewOfFile(pMap->pMapping);
pMap->pMapping = NULL;
}
if(pMap->hMapping)
{
CloseHandle(pMap->hMapping);
pMap->hMapping = NULL;
}
if(pMap->hHistoryFile)
{
CloseHandle(pMap->hHistoryFile);
pMap->hHistoryFile = NULL;
}
return DestroyCredentialHistoryMap(pMap);
}
DWORD
AppendCredentialHistoryMap(
PCREDENTIAL_HISTORY_MAP pMap,
PCREDENTIAL_HISTORY pCredHistory,
DWORD cbCredHistory
)
{
DWORD dwLastError = ERROR_SUCCESS;
DWORD dwHighFileSize = 0;
DWORD dwLowFileSize = 0;
HANDLE hWriteMapping = NULL;
LPVOID pWriteMapping = NULL;
PCREDENTIAL_HISTORY_HEADER pHeader = NULL;
if((NULL == pCredHistory) ||
(NULL == pMap))
{
return ERROR_INVALID_PARAMETER;
}
dwLowFileSize = pMap->dwMapSize + cbCredHistory;
if(dwLowFileSize < pMap->dwMapSize)
{
//we wrapped, so fail.
dwLastError = ERROR_INVALID_DATA;
goto error;
}
// Create a new mapping
hWriteMapping = CreateFileMapping(pMap->hHistoryFile,
NULL,
PAGE_READWRITE,
0,
dwLowFileSize,
NULL);
if(NULL == hWriteMapping)
{
dwLastError = GetLastError();
goto error;
}
pWriteMapping = (PCREDENTIAL_HISTORY_HEADER)(PBYTE)MapViewOfFile(hWriteMapping,
FILE_MAP_WRITE,
0,
0,
dwLowFileSize);
if(NULL == pWriteMapping)
{
dwLastError = GetLastError();
goto error;
}
//
// Append the rest of the current entry
//
CopyMemory((PBYTE)pWriteMapping + pMap->dwMapSize,
(PBYTE)pCredHistory + sizeof(CREDENTIAL_HISTORY_HEADER),
cbCredHistory - sizeof(CREDENTIAL_HISTORY_HEADER));
pHeader = (PCREDENTIAL_HISTORY_HEADER)((PBYTE)pWriteMapping +
pMap->dwMapSize +
cbCredHistory -
sizeof(CREDENTIAL_HISTORY_HEADER));
//
// Write a fresh header into the cred history file
//
pHeader->dwPreviousCredOffset = cbCredHistory;
pHeader->dwVersion = CREDENTIAL_HISTORY_VERSION;
dwLastError = UuidCreate( &pHeader->CredentialID );
pMap->dwMapSize = dwLowFileSize;
//
// Flush and close write mapping
//
if(ERROR_SUCCESS == dwLastError)
{
if(!FlushViewOfFile(pWriteMapping, pMap->dwMapSize))
{
dwLastError = GetLastError();
}
}
UnmapViewOfFile(pWriteMapping);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
CloseHandle(hWriteMapping);
hWriteMapping = NULL;
// Remap the read mapping to bump up the size
if(pMap->pMapping)
{
UnmapViewOfFile(pMap->pMapping);
pMap->pMapping = NULL;
}
if(pMap->hMapping)
{
CloseHandle(pMap->hMapping);
pMap->hMapping = NULL;
}
pMap->hMapping = CreateFileMapping(pMap->hHistoryFile,
NULL,
PAGE_READONLY,
dwHighFileSize,
pMap->dwMapSize,
NULL);
if(NULL == pMap->hMapping)
{
dwLastError = GetLastError();
goto error;
}
pMap->pMapping = (PBYTE)MapViewOfFile(pMap->hMapping,
FILE_MAP_READ,
0,
0,
0);
if(NULL == pMap->pMapping)
{
dwLastError = GetLastError();
goto error;
}
error:
if(hWriteMapping)
{
CloseHandle(hWriteMapping);
}
return dwLastError;
}
DWORD
DPAPIChangePassword(
HANDLE hUserToken,
BYTE OldPasswordShaOWF[A_SHA_DIGEST_LEN],
BYTE OldPasswordNTOWF[A_SHA_DIGEST_LEN],
BYTE NewPasswordOWF[A_SHA_DIGEST_LEN])
{
DWORD dwLastError = ERROR_SUCCESS;
PCREDENTIAL_HISTORY_MAP pMap = NULL;
PCREDENTIAL_HISTORY pHistory = NULL;
DWORD cbHistory = 0;
WCHAR wszUserSid[MAX_PATH+1];
DWORD cchUserSid = 0;
BYTE NewEncryptingCred[A_SHA_DIGEST_LEN];
WCHAR szProfilePath[MAX_PATH + 1];
HANDLE hOldUser = NULL;
D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword\n"));
//
// Get path to user's profile data. This will typically look something
// like "c:\Documents and Settings\<user>\Application Data".
//
dwLastError = PRGetProfilePath(hUserToken,
szProfilePath);
if( dwLastError != ERROR_SUCCESS )
{
D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword returned 0x%x\n", dwLastError));
return dwLastError;
}
//
// work-around the fact that much of this (new) code is not thread-safe.
//
RtlEnterCriticalSection(&g_csCredHistoryCache);
dwLastError = CreateCredentialHistoryMap(
hUserToken,
szProfilePath,
&pMap,
FALSE);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
//
// Get textual SID of user.
//
cchUserSid = MAX_PATH;
if(!GetUserTextualSid(hUserToken,
wszUserSid,
&cchUserSid))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
//
// Encrypt the credential history goo.
//
if(hUserToken)
{
if(!OpenThreadToken(GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_READ,
TRUE,
&hOldUser))
{
hOldUser = NULL;
}
if(!ImpersonateLoggedOnUser(hUserToken))
{
dwLastError = GetLastError();
goto error;
}
}
DeriveWithHMAC_SHA1(NewPasswordOWF,
A_SHA_DIGEST_LEN,
(PBYTE)wszUserSid,
cchUserSid*sizeof(WCHAR),
NewEncryptingCred);
dwLastError = EncryptCredentialHistory(NewEncryptingCred,
USE_DPAPI_OWF,
OldPasswordShaOWF,
OldPasswordNTOWF,
&pHistory,
&cbHistory);
if(hOldUser)
{
//
// This code probably never gets executed. hOldUser should be NULL, as this routine
// looks like called in the SYSTEM context.
// Even if the hOldUser is not NULL, the failure of SetThreadToken should no prevent
// continue execution of this routine. Let's ignore the return value of SetThreadToken
// here.
//
(void) SetThreadToken(NULL, hOldUser);
}
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
//
// Update the CREDHIST file.
//
dwLastError = AppendCredentialHistoryMap(pMap, pHistory, cbHistory);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
error:
if(hOldUser)
{
CloseHandle(hOldUser);
hOldUser = NULL;
}
if(pMap)
{
CloseCredentialHistoryMap(pMap, FALSE);
}
RtlLeaveCriticalSection(&g_csCredHistoryCache);
if(pHistory)
{
RtlSecureZeroMemory(pHistory, cbHistory);
LocalFree(pHistory);
}
RtlSecureZeroMemory(NewEncryptingCred, A_SHA_DIGEST_LEN);
D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword returned 0x%x\n", dwLastError));
return dwLastError;
}
#define SIGNATURE_SALT_SIZE (16)
#define CRED_SIGNATURE_VERSION 1
typedef struct _CRED_SIGNATURE
{
DWORD dwVersion;
GUID CredentialID;
DWORD cIterations;
BYTE Salt[SIGNATURE_SALT_SIZE];
DWORD cbSid;
DWORD cbSignature;
} CRED_SIGNATURE, *PCRED_SIGNATURE;
DWORD
LogonCredGenerateSignatureKey(
IN LUID *pLogonId,
IN DWORD dwFlags,
IN PBYTE pbCurrentOWF,
IN PCRED_SIGNATURE pSignature,
OUT BYTE rgbSignatureKey[A_SHA_DIGEST_LEN])
{
DWORD dwLastError = ERROR_SUCCESS;
BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN];
D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignatureKey\n"));
if(NULL == pbCurrentOWF)
{
dwLastError = QueryDerivedCredential(&pSignature->CredentialID,
pLogonId,
dwFlags,
(PBYTE)(pSignature+1),
pSignature->cbSid,
rgbDerivedCredential);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
}
else
{
// D_DebugLog((DEB_TRACE_BUFFERS, "Input CurrentOWF:\n"));
// D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pbCurrentOWF, A_SHA_DIGEST_LEN);
DeriveWithHMAC_SHA1(pbCurrentOWF,
A_SHA_DIGEST_LEN,
(PBYTE)(pSignature+1),
pSignature->cbSid,
rgbDerivedCredential);
if(dwFlags & USE_ROOT_CREDENTIAL)
{
ZeroMemory(&pSignature->CredentialID, sizeof(GUID));
}
}
// D_DebugLog((DEB_TRACE_BUFFERS, "Computed DerivedCredential:\n"));
// D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", rgbDerivedCredential, sizeof(rgbDerivedCredential));
if(!PKCS5DervivePBKDF2( rgbDerivedCredential,
A_SHA_DIGEST_LEN,
pSignature->Salt,
SIGNATURE_SALT_SIZE,
CALG_HMAC,
pSignature->cIterations,
1,
rgbSignatureKey))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
// D_DebugLog((DEB_TRACE_BUFFERS, "Computed SignatureKey:\n"));
// D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", rgbSignatureKey, A_SHA_DIGEST_LEN);
error:
RtlSecureZeroMemory(rgbDerivedCredential, A_SHA_DIGEST_LEN);
D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignatureKey returned 0x%x\n", dwLastError));
return dwLastError;
}
DWORD
LogonCredGenerateSignature(
IN HANDLE hUserToken,
IN PBYTE pbData,
IN DWORD cbData,
IN PBYTE pbCurrentOWF,
OUT PBYTE *ppbSignature,
OUT DWORD *pcbSignature)
{
DWORD dwLastError = ERROR_SUCCESS;
LUID LogonId;
PCRED_SIGNATURE pSignature = NULL;
DWORD cbSignature = 0;
BYTE rgbSignatureKey[A_SHA_DIGEST_LEN];
PSID pUserSid = NULL;
D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignature\n"));
cbSignature = sizeof(CRED_SIGNATURE);
if(!GetTokenAuthenticationId(hUserToken, &LogonId))
{
dwLastError = GetLastError();
goto error;
}
if(!GetTokenUserSid(hUserToken, &pUserSid))
{
dwLastError = GetLastError();
goto error;
}
D_DebugLog((DEB_TRACE_BUFFERS, "User SID:\n"));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)pUserSid, GetLengthSid(pUserSid));
cbSignature += GetLengthSid(pUserSid);
cbSignature += A_SHA_DIGEST_LEN;
pSignature = (PCRED_SIGNATURE)LocalAlloc(LMEM_ZEROINIT, cbSignature);
if(NULL == pSignature)
{
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
goto error;
}
pSignature->cIterations = GetIterationCount();
pSignature->dwVersion = CRED_SIGNATURE_VERSION;
pSignature->cbSid = GetLengthSid(pUserSid);
pSignature->cbSignature = A_SHA_DIGEST_LEN;
CopyMemory((PBYTE)(pSignature+1),
pUserSid,
pSignature->cbSid);
if(!RtlGenRandom(pSignature->Salt, SIGNATURE_SALT_SIZE))
{
dwLastError = GetLastError();
goto error;
}
// D_DebugLog((DEB_TRACE_BUFFERS, "Generated Salt:\n"));
// D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pSignature->Salt, SIGNATURE_SALT_SIZE);
dwLastError = LogonCredGenerateSignatureKey(&LogonId,
USE_ROOT_CREDENTIAL | USE_DPAPI_OWF,
pbCurrentOWF,
pSignature,
rgbSignatureKey);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
if(!FMyPrimitiveHMACParam(
rgbSignatureKey,
A_SHA_DIGEST_LEN,
pbData,
cbData,
(PBYTE)(pSignature+1) + pSignature->cbSid))
{
dwLastError = ERROR_INVALID_DATA;
}
else
{
D_DebugLog((DEB_TRACE_BUFFERS, "Computed Signature:\n"));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)(pSignature+1) + pSignature->cbSid, A_SHA_DIGEST_LEN);
*ppbSignature = (PBYTE)pSignature;
*pcbSignature = cbSignature;
pSignature = NULL;
}
error:
if(pSignature)
{
RtlSecureZeroMemory(pSignature, cbSignature);
LocalFree(pSignature);
}
if(pUserSid)
{
SSFree(pUserSid);
}
D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignature returned 0x%x\n", dwLastError));
return dwLastError;
}
DWORD
LogonCredVerifySignature(
IN HANDLE hUserToken, // optional
IN PBYTE pbData,
IN DWORD cbData,
IN PBYTE pbCurrentOWF,
IN PBYTE pbSignature,
IN DWORD cbSignature)
{
DWORD dwLastError = ERROR_SUCCESS;
LUID LogonId;
HANDLE hOldUser = NULL;
BOOL fIsMember = FALSE;
PCRED_SIGNATURE pSignature = (PCRED_SIGNATURE)pbSignature;
BYTE rgbSignatureKey[A_SHA_DIGEST_LEN];
BYTE rgbSignatureHash[A_SHA_DIGEST_LEN];
PSID pUserSid = NULL;
D_DebugLog((DEB_TRACE_API, "LogonCredVerifySignature\n"));
if(hUserToken)
{
if(!OpenThreadToken(GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_READ,
TRUE,
&hOldUser))
{
hOldUser = NULL;
}
if(!ImpersonateLoggedOnUser(hUserToken))
{
dwLastError = GetLastError();
goto error;
}
}
if(!GetThreadAuthenticationId(GetCurrentThread(), &LogonId))
{
dwLastError = GetLastError();
goto error;
}
//
// Verify passed in credential
//
if((NULL == pSignature) ||
(sizeof(CRED_SIGNATURE) > cbSignature) ||
(pSignature->dwVersion != CRED_SIGNATURE_VERSION) ||
(pSignature->cbSid + pSignature->cbSignature + sizeof(CRED_SIGNATURE) > cbSignature) ||
(pSignature->cbSignature != A_SHA_DIGEST_LEN))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
if(!IsValidSid((PSID)(pSignature+1)))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
if(pSignature->cbSid != GetLengthSid((PSID)(pSignature+1)))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
D_DebugLog((DEB_TRACE_BUFFERS, "User SID:\n"));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)(pSignature+1), pSignature->cbSid);
if(!CheckTokenMembership( NULL,
(PSID)(pSignature+1),
&fIsMember ))
{
dwLastError = GetLastError();
goto error;
}
if(!fIsMember)
{
dwLastError = ERROR_INVALID_ACCESS;
goto error;
}
dwLastError = LogonCredGenerateSignatureKey(&LogonId,
USE_DPAPI_OWF,
pbCurrentOWF,
pSignature,
rgbSignatureKey);
if(ERROR_SUCCESS != dwLastError)
{
goto error;
}
if(!FMyPrimitiveHMACParam(
rgbSignatureKey,
A_SHA_DIGEST_LEN,
pbData,
cbData,
rgbSignatureHash))
{
dwLastError = ERROR_INVALID_DATA;
goto error;
}
D_DebugLog((DEB_TRACE_BUFFERS, "Computed Signature:\n"));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", rgbSignatureHash, A_SHA_DIGEST_LEN);
D_DebugLog((DEB_TRACE_BUFFERS, "Input Signature:\n"));
D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)(pSignature+1) + pSignature->cbSid, pSignature->cbSignature);
if(0 != memcmp(rgbSignatureHash, (PBYTE)(pSignature+1) + pSignature->cbSid, pSignature->cbSignature))
{
D_DebugLog((DEB_ERROR, "LogonCredVerifySignature: signature did not verify!\n"));
dwLastError = ERROR_INVALID_ACCESS;
goto error;
}
error:
if(hOldUser)
{
//
// We have already done our job. SetThreadToken failure should be ignored here.
// Further, this code might never been executed. LogonCredVerifySignature() is called
// from two places. If hUserToken==NULL, -> hOldUser = NULL (the thread was impersonating)
// If hUserToken != NULL, the caller is in SYSTEM, OpenThreadToken would fail -> hOldUser == NULL.
// Either way, hOldUser should be NULL.
//
(void) SetThreadToken(NULL, hOldUser);
CloseHandle(hOldUser);
}
D_DebugLog((DEB_TRACE_API, "LogonCredVerifySignature returned 0x%x\n", dwLastError));
return dwLastError;
}
DWORD
DPAPINotifyPasswordChange(
IN PUNICODE_STRING NetbiosDomainName,
IN PUNICODE_STRING UserName,
IN PUNICODE_STRING OldPassword,
IN PUNICODE_STRING NewPassword)
{
DWORD Status = ERROR_SUCCESS;
BYTE OldPasswordShaOWF[A_SHA_DIGEST_LEN];
BYTE OldPasswordNTOWF[A_SHA_DIGEST_LEN];
BYTE NewPasswordOWF[A_SHA_DIGEST_LEN];
HANDLE hThreadToken = NULL;
HANDLE hUserToken = NULL;
PWSTR pszTargetName = NULL;
PWSTR pszCurrentName = NULL;
DWORD cchCurrentName;
PSID pUserSid = NULL;
PSID pCurrentSid = NULL;
DWORD cbSid;
SID_NAME_USE SidType;
PWSTR pszDomainName = NULL;
DWORD cchDomainName;
BOOL fSameUser = FALSE;
BOOL fLocalAccount = FALSE;
PROFILEINFOW ProfileInfo;
BOOL fProfileLoaded = FALSE;
D_DebugLog((DEB_TRACE_API, "DPAPINotifyPasswordChange\n"));
//
// Validate input parameters.
//
if((NetbiosDomainName == NULL) ||
(UserName == NULL) ||
(NewPassword == NULL))
{
Status = ERROR_INVALID_PARAMETER;
goto cleanup;
}
if(NetbiosDomainName->Buffer)
{
D_DebugLog((DEB_TRACE_API, " Domain:%ls\n", NetbiosDomainName->Buffer));
}
if(UserName->Buffer)
{
D_DebugLog((DEB_TRACE_API, " Username:%ls\n", UserName->Buffer));
}
#ifdef COMPILED_BY_DEVELOPER
if(OldPassword)
{
D_DebugLog((DEB_TRACE_API, " Old password:%ls\n", OldPassword->Buffer));
}
D_DebugLog((DEB_TRACE_API, " New password:%ls\n", NewPassword->Buffer));
#endif
//
// Save off the token for the current thread, and revert back to the
// local system account.
//
if(!OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY | TOKEN_IMPERSONATE,
FALSE,
&hThreadToken))
{
hThreadToken = NULL;
}
RevertToSelf();
//
// Get SID of user whose password is being changed.
//
cbSid = 0;
if(!LookupAccountName(NetbiosDomainName->Buffer,
UserName->Buffer,
NULL,
&cbSid,
NULL,
&cchDomainName,
&SidType))
{
Status = GetLastError();
if(Status != ERROR_INSUFFICIENT_BUFFER)
{
goto cleanup;
}
}
pUserSid = LocalAlloc(LPTR, cbSid);
if(pUserSid == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
pszDomainName = (PWSTR)LocalAlloc(LPTR, cchDomainName * sizeof(WCHAR));
if(pszDomainName == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
if(!LookupAccountName(NetbiosDomainName->Buffer,
UserName->Buffer,
pUserSid,
&cbSid,
pszDomainName,
&cchDomainName,
&SidType))
{
Status = GetLastError();
if(Status != ERROR_INSUFFICIENT_BUFFER)
{
goto cleanup;
}
}
//
// Determine if we're already logged on as the user whose password
// is being changed. If we are, then we can skip loading the user
// profile, etc.
//
if(hThreadToken != NULL)
{
if(!GetTokenUserSid(hThreadToken, &pCurrentSid))
{
Status = GetLastError();
goto cleanup;
}
if(EqualSid(pCurrentSid, pUserSid))
{
fSameUser = TRUE;
}
}
//
// Create logon token for user whose password is being changed.
//
if(fSameUser)
{
hUserToken = hThreadToken;
}
else
{
D_DebugLog((DEB_TRACE, "Logging on as user whose password is being changed.\n"));
if(!LogonUser(UserName->Buffer,
NetbiosDomainName->Buffer,
NewPassword->Buffer,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hUserToken))
{
Status = GetLastError();
D_DebugLog((DEB_ERROR, "Unable to log on as user whose password is being changed (0x%x).\n", Status));
goto cleanup;
}
memset(&ProfileInfo, 0, sizeof(ProfileInfo));
ProfileInfo.dwSize = sizeof(ProfileInfo);
ProfileInfo.dwFlags = PI_NOUI;
ProfileInfo.lpUserName = UserName->Buffer;
if(!LoadUserProfileW(hUserToken, &ProfileInfo))
{
Status = GetLastError();
D_DebugLog((DEB_ERROR, "Error loading user profile (0x%x).\n", Status));
goto cleanup;
}
fProfileLoaded = TRUE;
}
//
// Is this a local account?
//
if(NetbiosDomainName->Buffer)
{
WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD cchMachineName;
cchMachineName = MAX_COMPUTERNAME_LENGTH + 1;
if(!GetComputerName(szMachineName, &cchMachineName))
{
Status = GetLastError();
goto cleanup;
}
if (CSTR_EQUAL == CompareString(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
NetbiosDomainName->Buffer,
-1, // cchCount1
szMachineName,
-1 // cchCount2
))
{
fLocalAccount = TRUE;
}
}
if(fLocalAccount)
{
D_DebugLog((DEB_TRACE, "Local account\n"));
if(OldPassword == NULL)
{
Status = ERROR_INVALID_PARAMETER;
goto cleanup;
}
//
// Compute hashes of old and new passwords.
//
ZeroMemory(OldPasswordShaOWF, A_SHA_DIGEST_LEN);
ZeroMemory(OldPasswordNTOWF, A_SHA_DIGEST_LEN);
ZeroMemory(NewPasswordOWF, A_SHA_DIGEST_LEN);
FMyPrimitiveSHA(
(PBYTE)OldPassword->Buffer,
OldPassword->Length,
OldPasswordShaOWF);
Status = RtlCalculateNtOwfPassword(
OldPassword,
(PLM_OWF_PASSWORD)OldPasswordNTOWF);
if(Status != ERROR_SUCCESS)
{
goto cleanup;
}
FMyPrimitiveSHA(
(PBYTE)NewPassword->Buffer,
NewPassword->Length,
NewPasswordOWF);
#ifdef COMPILED_BY_DEVELOPER
D_DebugLog((DEB_TRACE, " Old password SHA OWF:\n"));
D_DPAPIDumpHexData(DEB_TRACE, " ", OldPasswordShaOWF, A_SHA_DIGEST_LEN);
D_DebugLog((DEB_TRACE, " Old password NT OWF:\n"));
D_DPAPIDumpHexData(DEB_TRACE, " ", OldPasswordNTOWF, A_SHA_DIGEST_LEN);
D_DebugLog((DEB_TRACE, " New password SHA OWF:\n"));
D_DPAPIDumpHexData(DEB_TRACE, " ", NewPasswordOWF, A_SHA_DIGEST_LEN);
#endif
//
// Encrypt the CREDHIST file with the new password and append the new password to
// the end of the file.
//
Status = DPAPIChangePassword(hUserToken,
OldPasswordShaOWF,
OldPasswordNTOWF,
NewPasswordOWF);
if(Status != ERROR_SUCCESS)
{
goto cleanup;
}
//
// Re-synchronize the master keys.
//
DPAPISynchronizeMasterKeys(hUserToken);
//
// Encrypt the new password with the recovery public key, and store it
// in the RECOVERY file. This will allow us to recover the password using the
// recovery floppy, should the user forget it.
//
Status = RecoverChangePasswordNotify(hUserToken,
OldPasswordShaOWF,
NewPassword);
if(Status != ERROR_SUCCESS)
{
goto cleanup;
}
}
else
{
D_DebugLog((DEB_TRACE, "Domain account\n"));
//
// Re-synchronize the master keys.
//
DPAPISynchronizeMasterKeys(hUserToken);
}
cleanup:
if(pUserSid) LocalFree(pUserSid);
if(pCurrentSid) LocalFree(pCurrentSid);
if(pszDomainName) LocalFree(pszDomainName);
if(pszTargetName) LocalFree(pszTargetName);
if(pszCurrentName) LocalFree(pszCurrentName);
if(hUserToken && fProfileLoaded)
{
UnloadUserProfile(hUserToken, ProfileInfo.hProfile);
}
if(hUserToken && (hUserToken != hThreadToken))
{
CloseHandle(hUserToken);
}
if(hThreadToken)
{
if(!ImpersonateLoggedOnUser(hThreadToken))
{
// Unable to impersonate user.
if(Status == ERROR_SUCCESS)
{
Status = GetLastError();
}
}
CloseHandle(hThreadToken);
}
D_DebugLog((DEB_TRACE_API, "DPAPINotifyPasswordChange returned 0x%x\n", Status));
return Status;
}