|
|
// Users.cpp: implementation of the CUsers class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "efsadu.h"
#include "Users.h"
#include <wincrypt.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CUsers::CUsers() { m_UsersRoot = NULL; m_UserAddedCnt = 0; m_UserRemovedCnt = 0; }
//////////////////////////////////////////////////////////////////////
// Walk through the chain to free the memory
//////////////////////////////////////////////////////////////////////
CUsers::~CUsers() { Clear(); }
PUSERSONFILE CUsers::RemoveItemFromHead(void) { PUSERSONFILE PItem = m_UsersRoot; if (m_UsersRoot){ m_UsersRoot = m_UsersRoot->Next; if ((PItem->Flag & USERADDED) && !(PItem->Flag & USERREMOVED)){ m_UserAddedCnt--; } if ((PItem->Flag & USERINFILE) && (PItem->Flag & USERREMOVED)){ m_UserRemovedCnt--; } } return PItem; }
DWORD CUsers::Add( CUsers &NewUsers ) { PUSERSONFILE NewItem;
while (NewItem = NewUsers.RemoveItemFromHead()){ PUSERSONFILE TmpItem = m_UsersRoot; while ( TmpItem ){
if ((NewItem->UserName && TmpItem->UserName && !_tcsicmp(NewItem->UserName, TmpItem->UserName)) || ((NULL == NewItem->UserName) && (TmpItem->UserName == NULL))){
//
// User exist
//
if ( TmpItem->Flag & USERREMOVED ){
if ( TmpItem->Flag & USERADDED ){
ASSERT(!(TmpItem->Flag & USERINFILE));
//
// User added and removed
//
m_UserAddedCnt++;
} else if ( TmpItem->Flag & USERINFILE ){
//
// User added and removed
//
m_UserRemovedCnt--;
} TmpItem->Flag &= ~USERREMOVED; }
//
// The caller will count on CUsers to release the memory
//
if (NewItem->UserName){ delete [] NewItem->UserName; } if ( NewItem->Context ) { CertFreeCertificateContext((PCCERT_CONTEXT)NewItem->Context); } delete [] NewItem->Cert; if (NewItem->UserSid){ delete [] NewItem->UserSid; } delete NewItem; NewItem = NULL; break; } TmpItem = TmpItem->Next; }
if (NewItem ){ //
// New item. Insert into the head.
//
NewItem->Next = m_UsersRoot; m_UsersRoot = NewItem; m_UserAddedCnt++; }
}
return ERROR_SUCCESS; }
DWORD CUsers::Add( LPTSTR UserName, PVOID UserCert, PSID UserSid, /* = NULL */ DWORD Flag, /* = USERINFILE */ PVOID Context /* = NULL */ ) //////////////////////////////////////////////////////////////////////
// Routine Description:
// Create an item for a user
// Arguments:
// UserName -- User's name
// UserCert -- User's certificate blob or hash
// UserSid -- User's ID. Can be NULL
// Flag -- Indicate if the item is existing in the file, to be added or removed
// Return Value:
// NO_ERROR if succeed.
// Will throw exception if memory allocation fails. ( From new.)
//
//////////////////////////////////////////////////////////////////////
{
PUSERSONFILE UserItem; PUSERSONFILE TmpUserItem = m_UsersRoot; PEFS_CERTIFICATE_BLOB CertBlob; PEFS_HASH_BLOB CertHashBlob; DWORD CertSize; DWORD SidSize;
if ( !UserCert ){ return ERROR_INVALID_PARAMETER; }
ASSERT ( (( Flag & USERADDED ) || ( Flag & USERINFILE )) && ( (Flag & (USERADDED | USERINFILE)) != (USERADDED | USERINFILE)));
//
// If the user already in the memory, no new item is to be created except for unknown user
//
while ( TmpUserItem ){ if ( (UserName && TmpUserItem->UserName && !_tcsicmp(UserName, TmpUserItem->UserName)) || ((NULL == UserName) && (TmpUserItem->UserName == NULL))){
//
// User exist
//
if ( TmpUserItem->Flag & USERREMOVED ){
if ( TmpUserItem->Flag & USERADDED ){
ASSERT(!(TmpUserItem->Flag & USERINFILE));
//
// User added and removed
//
m_UserAddedCnt++;
} else if ( TmpUserItem->Flag & USERINFILE ){
//
// User added and removed
//
m_UserRemovedCnt--;
} TmpUserItem->Flag &= ~USERREMOVED; }
//
// The caller will count on CUsers to release
// the context if the call returns CRYPT_E_EXISTS. This is just for
// performance reason.
//
/*
if (UserName){ delete [] UserName; } */ if ( Context ) { CertFreeCertificateContext((PCCERT_CONTEXT)Context); Context = NULL; } return CRYPT_E_EXISTS; } TmpUserItem = TmpUserItem->Next; } try { UserItem = new USERSONFILE; if ( NULL == UserItem ){ AfxThrowMemoryException( ); }
UserItem->Next = NULL;
//
// In case exception raised, we can call delete.
// Delete NULL is OK, but random data is not OK.
//
UserItem->UserSid = NULL; UserItem->Cert = NULL; UserItem->Context = NULL;
if ( UserSid ){ SidSize = GetLengthSid( UserSid ); if ( SidSize > 0 ){ UserItem->UserSid = new BYTE[SidSize]; if ( NULL == UserItem->UserSid ){ AfxThrowMemoryException( ); } if ( !CopySid(SidSize, UserItem->UserSid, UserSid)){ delete [] UserItem->UserSid; delete UserItem; return GetLastError(); } } else { delete UserItem; return GetLastError(); } } else { UserItem->UserSid = NULL; } if ( Flag & USERINFILE ){
//
// The info is from the file. Use the hash structure
//
CertHashBlob = ( PEFS_HASH_BLOB ) UserCert; CertSize = sizeof(EFS_HASH_BLOB) + CertHashBlob->cbData; UserItem->Cert = new BYTE[CertSize]; if ( NULL == UserItem->Cert ){ AfxThrowMemoryException( ); } ((PEFS_HASH_BLOB)UserItem->Cert)->cbData = CertHashBlob->cbData; ((PEFS_HASH_BLOB)UserItem->Cert)->pbData = (PBYTE)(UserItem->Cert) + sizeof(EFS_HASH_BLOB); memcpy(((PEFS_HASH_BLOB)UserItem->Cert)->pbData, CertHashBlob->pbData, CertHashBlob->cbData ); } else {
//
// The info is from the user picked cert. Use Cert Blob structure
//
CertBlob = ( PEFS_CERTIFICATE_BLOB ) UserCert; CertSize = sizeof(EFS_CERTIFICATE_BLOB) + CertBlob->cbData; UserItem->Cert = new BYTE[CertSize]; if ( NULL == UserItem->Cert ){ AfxThrowMemoryException( ); } ((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->cbData = CertBlob->cbData; ((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->dwCertEncodingType = CertBlob->dwCertEncodingType; ((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->pbData = (PBYTE)(UserItem->Cert) + sizeof(EFS_CERTIFICATE_BLOB); memcpy(((PEFS_CERTIFICATE_BLOB)UserItem->Cert)->pbData, CertBlob->pbData, CertBlob->cbData );
} UserItem->UserName = UserName; UserItem->Context = Context; UserItem->Flag = Flag; if ( Flag & USERADDED ){ m_UserAddedCnt ++; } } catch (...) { delete [] UserItem->UserSid; delete [] UserItem->Cert; delete UserItem; AfxThrowMemoryException( ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Add to the head
//
if ( NULL != m_UsersRoot ){ UserItem->Next = m_UsersRoot; } m_UsersRoot = UserItem;
return NO_ERROR; }
DWORD CUsers::Remove( LPCTSTR UserName ) //////////////////////////////////////////////////////////////////////
// Routine Description:
// Remove a user from the list. Actually just mark for remove.
// Arguments:
// UserName -- User's name
// Return Value:
// NO_ERROR if succeed.
// ERROR_NOT_FOUND if the user cannot be found.
//
//////////////////////////////////////////////////////////////////////
{ PUSERSONFILE TmpUserItem = m_UsersRoot;
BOOL UserMatched =FALSE;
while ( TmpUserItem ){ if (((NULL==UserName) && ( NULL == TmpUserItem->UserName)) || ( UserName && TmpUserItem->UserName && !_tcsicmp(UserName, TmpUserItem->UserName))){
//
// User exist, mark it for remove
//
if ( TmpUserItem->Flag & USERINFILE ){ m_UserRemovedCnt++; } else if ( TmpUserItem->Flag & USERADDED ) { m_UserAddedCnt--; } TmpUserItem->Flag |= USERREMOVED; return NO_ERROR; } TmpUserItem = TmpUserItem->Next; } return ERROR_NOT_FOUND; }
DWORD CUsers::GetCertInfo( LPCTSTR UserName, PVOID *CertData, PVOID *CertContext ) //////////////////////////////////////////////////////////////////////
// Routine Description:
// Get user's cert related value
// Arguments:
// UserName -- User's name whose cert is to be found
// CertData -- Certificate Thumbprinter or cert blob
// CertContext -- Cert context
// Return Value:
// Win32 Error
//
//////////////////////////////////////////////////////////////////////
{
PUSERSONFILE TmpUserItem = m_UsersRoot;
BOOL UserMatched =FALSE;
if ((CertData == NULL) || (CertContext == NULL) ) { return ERROR_INVALID_PARAMETER; }
while ( TmpUserItem ){ if (((NULL==UserName) && ( NULL == TmpUserItem->UserName)) || ( UserName && TmpUserItem->UserName && !_tcsicmp(UserName, TmpUserItem->UserName))){
//
// User exist, return the interested data
//
*CertData = TmpUserItem->Cert; *CertContext = TmpUserItem->Context;
return ERROR_SUCCESS; } TmpUserItem = TmpUserItem->Next; } return ERROR_NOT_FOUND; }
PVOID CUsers::StartEnum() //////////////////////////////////////////////////////////////////////
// Routine Description:
// Prepare for GetNextUser
// Arguments:
//
// Return Value:
// A pointer used for GetNextUser
//
//////////////////////////////////////////////////////////////////////
{ return ((PVOID)m_UsersRoot); }
PVOID CUsers::GetNextUser( PVOID Token, CString &UserName, CString &CertHash ) //////////////////////////////////////////////////////////////////////
// Routine Description:
// Get next user in the list.(Not removed).
// Arguments:
// UserName -- Next User's name
// CertHash -- Certificate Thumbprinter
// Token -- A pointer returned by previous GetNextUser or StartEnum.
// Return Value:
// A pointer for GetNextUser()
//
//////////////////////////////////////////////////////////////////////
{
PUSERSONFILE TmpItem = (PUSERSONFILE) Token; PVOID RetPointer = NULL;
while ( TmpItem ){
if ( TmpItem->Flag & USERREMOVED ){ TmpItem = TmpItem->Next; continue; }
try{ LPWSTR HashString = NULL;
UserName = TmpItem->UserName;
if (TmpItem->Flag & USERINFILE){
PEFS_HASH_BLOB UserHashBlob;
UserHashBlob = (PEFS_HASH_BLOB)TmpItem->Cert; HashString = new WCHAR[((((UserHashBlob->cbData + 1)/2) * 5) + 1)]; if (HashString) { ConvertHashToStr(UserHashBlob->pbData, UserHashBlob->cbData, HashString); }
} else if ( TmpItem->Context ){
DWORD cbHash; PBYTE pbHash;
if (CertGetCertificateContextProperty( (PCCERT_CONTEXT)TmpItem->Context, CERT_HASH_PROP_ID, NULL, &cbHash )) {
pbHash = (PBYTE)new BYTE[cbHash];
if (pbHash != NULL) {
if (CertGetCertificateContextProperty( (PCCERT_CONTEXT)TmpItem->Context, CERT_HASH_PROP_ID, pbHash, &cbHash )) {
HashString = new WCHAR[((((cbHash + 1)/2) * 5) + 1)]; if (HashString) { ConvertHashToStr(pbHash, cbHash, HashString); } } delete [] pbHash;
} }
} CertHash = HashString; if (HashString){ delete [] HashString; } RetPointer = TmpItem->Next; } catch (...){
//
// Out of memory
//
TmpItem = NULL; RetPointer = NULL; } break; }
if ( NULL == TmpItem ){ UserName.Empty(); CertHash.Empty(); } return RetPointer;
}
DWORD CUsers::GetUserAddedCnt() { return m_UserAddedCnt; }
DWORD CUsers::GetUserRemovedCnt() { return m_UserRemovedCnt; }
PVOID CUsers::GetNextChangedUser( PVOID Token, LPTSTR * UserName, PSID * UserSid, PVOID * CertData, DWORD * Flag ) //////////////////////////////////////////////////////////////////////
// Routine Description:
// Get the info for changed users. This method is not well behaved in the
// sense of OOP. It exposes internal pointers to the ouside world. The gain
// is performance. At this moment, CUsers is a supporting class and used only
// by USERLIST and CAddSheet (single thread). We can make USERLIST a
// friend of CUsers if such concerns are raised in the future or reimplement this.
// The same issue applies to the enumerate methods.
//
// Arguments:
// Token -- A pointer to the item returned in previous GetNextChangedUser or StartEnum.
// UserName -- User's name
// CertData -- User's certificate blob or hash
// UserSid -- User's ID. Can be NULL
// Flag -- Indicate if the item is existing in the file, to be added or removed
// Return Value:
// Next item pointer.
//
//////////////////////////////////////////////////////////////////////
{ BOOL ChangedUserFound = FALSE;
while ( Token ){
*Flag = ((PUSERSONFILE) Token)->Flag;
if ( ( *Flag & USERADDED ) && !( *Flag & USERREMOVED )){
//
// The user is to to be added to the file
//
*Flag = USERADDED; ChangedUserFound = TRUE;
} else if ( ( *Flag & USERREMOVED ) && ( *Flag & USERINFILE)){
//
// The user is to be removed from the file
//
*Flag = USERREMOVED; ChangedUserFound = TRUE;
}
if ( ChangedUserFound ){
*UserName = ((PUSERSONFILE) Token)->UserName; *UserSid = ((PUSERSONFILE) Token)->UserSid; *CertData = ((PUSERSONFILE) Token)->Cert; return ((PUSERSONFILE) Token)->Next;
} else {
Token = ((PUSERSONFILE) Token)->Next;
}
}
*UserName = NULL; *UserSid = NULL; *CertData = NULL; *Flag = 0; return NULL; }
void CUsers::Clear() {
PUSERSONFILE TmpUserItem = m_UsersRoot; while (TmpUserItem){ m_UsersRoot = TmpUserItem->Next; delete [] TmpUserItem->UserName; delete [] TmpUserItem->Cert; if (TmpUserItem->UserSid){ delete [] TmpUserItem->UserSid; } if (TmpUserItem->Context){ CertFreeCertificateContext((PCCERT_CONTEXT)TmpUserItem->Context); } delete TmpUserItem; TmpUserItem = m_UsersRoot; }
m_UsersRoot = NULL; m_UserAddedCnt = 0; m_UserRemovedCnt = 0;
}
void CUsers::ConvertHashToStr( PBYTE pHashData, DWORD cbData, LPWSTR OutHashStr ) {
DWORD Index = 0; BOOLEAN NoLastZero = FALSE;
for (; Index < cbData; Index+=2) {
BYTE HashByteLow = pHashData[Index] & 0x0f; BYTE HashByteHigh = (pHashData[Index] & 0xf0) >> 4;
OutHashStr[Index * 5/2] = HashByteHigh > 9 ? (WCHAR)(HashByteHigh - 9 + 0x40): (WCHAR)(HashByteHigh + 0x30); OutHashStr[Index * 5/2 + 1] = HashByteLow > 9 ? (WCHAR)(HashByteLow - 9 + 0x40): (WCHAR)(HashByteLow + 0x30);
if (Index + 1 < cbData) { HashByteLow = pHashData[Index+1] & 0x0f; HashByteHigh = (pHashData[Index+1] & 0xf0) >> 4; OutHashStr[Index * 5/2 + 2] = HashByteHigh > 9 ? (WCHAR)(HashByteHigh - 9 + 0x40): (WCHAR)(HashByteHigh + 0x30); OutHashStr[Index * 5/2 + 3] = HashByteLow > 9 ? (WCHAR)(HashByteLow - 9 + 0x40): (WCHAR)(HashByteLow + 0x30); OutHashStr[Index * 5/2 + 4] = L' ';
} else { OutHashStr[Index * 5/2 + 2] = 0; NoLastZero = TRUE; }
}
if (!NoLastZero) { OutHashStr[Index*5/2] = 0; }
}
|