//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997-2001. // // File: Users.cpp // // Contents: // //---------------------------------------------------------------------------- // Users.cpp: implementation of the CUsers class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Users.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #ifndef ALPHA #define new DEBUG_NEW #endif #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->m_pNext; if ((PItem->m_dwFlag & USERADDED) && !(PItem->m_dwFlag & USERREMOVED)){ m_UserAddedCnt--; } if ((PItem->m_dwFlag & USERINFILE) && (PItem->m_dwFlag & USERREMOVED)){ m_UserRemovedCnt--; } } return PItem; } DWORD CUsers::Add( CUsers &NewUsers ) { PUSERSONFILE NewItem = NewUsers.RemoveItemFromHead(); while ( NewItem ) { PUSERSONFILE TmpItem = m_UsersRoot; while ( TmpItem ) { if ((NewItem->m_szUserName && TmpItem->m_szUserName && !_tcsicmp(NewItem->m_szUserName, TmpItem->m_szUserName)) || ( !NewItem->m_szUserName && !TmpItem->m_szUserName)) { if ( !TmpItem->m_szUserName) { bool bUserMatched = false; if (( !NewItem->m_szDnName && !TmpItem->m_szDnName) || (NewItem->m_szDnName && TmpItem->m_szDnName && !_tcsicmp(NewItem->m_szDnName, TmpItem->m_szDnName))) { bUserMatched = true; } if ( !bUserMatched ) { TmpItem = TmpItem->m_pNext; continue; } } // // User exist // if ( TmpItem->m_dwFlag & USERREMOVED ) { if ( TmpItem->m_dwFlag & USERADDED ) { ASSERT(!(TmpItem->m_dwFlag & USERINFILE)); // // User added and removed // m_UserAddedCnt++; } else if ( TmpItem->m_dwFlag & USERINFILE ) { // // User added and removed // m_UserRemovedCnt--; } TmpItem->m_dwFlag &= ~USERREMOVED; } // // The caller will count on CUsers to release the memory // if (NewItem->m_szUserName) { delete [] NewItem->m_szUserName; } if (NewItem->m_szDnName) { delete [] NewItem->m_szDnName; } if ( NewItem->m_pCertContext ) { CertFreeCertificateContext(NewItem->m_pCertContext); } delete [] NewItem->m_pCert; if (NewItem->m_UserSid) { delete [] NewItem->m_UserSid; } delete NewItem; NewItem = NULL; break; } TmpItem = TmpItem->m_pNext; } if (NewItem ) { // // New item. Insert into the head. // NewItem->m_pNext = m_UsersRoot; m_UsersRoot = NewItem; m_UserAddedCnt++; } NewItem = NewUsers.RemoveItemFromHead(); } return ERROR_SUCCESS; } DWORD CUsers::Add( LPWSTR pszUserName, LPWSTR pszDnName, PVOID UserCert, PSID UserSid, /* = NULL */ DWORD dwFlag, /* = USERINFILE */ PCCERT_CONTEXT pCertContext /* = NULL */ ) ////////////////////////////////////////////////////////////////////// // Routine Description: // Create an item for a user // Arguments: // m_szUserName -- User's name // m_szDnName -- User's distinguished name // UserCert -- User's certificate blob or hash // m_UserSid -- User's ID. Can be NULL // m_dwFlag -- 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 = 0; PUSERSONFILE TmpUserItem = m_UsersRoot; PEFS_CERTIFICATE_BLOB CertBlob; PEFS_HASH_BLOB CertHashBlob; DWORD CertSize; DWORD SidSize; if ( !UserCert ) { return ERROR_INVALID_PARAMETER; } ASSERT ( (( dwFlag & USERADDED ) || ( dwFlag & USERINFILE )) && ( (dwFlag & (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 ( (pszUserName && TmpUserItem->m_szUserName && !_tcsicmp(pszUserName, TmpUserItem->m_szUserName)) || ((!pszUserName) && (TmpUserItem->m_szUserName == NULL))) { if (!pszUserName) { bool bUserMatched = false; if (( !pszDnName && !TmpUserItem->m_szDnName) || (pszDnName && TmpUserItem->m_szDnName && !_tcsicmp(pszDnName, TmpUserItem->m_szDnName))) { bUserMatched = true; } if ( !bUserMatched ) { TmpUserItem = TmpUserItem->m_pNext; continue; } } // // User exist // if ( TmpUserItem->m_dwFlag & USERREMOVED ) { if ( TmpUserItem->m_dwFlag & USERADDED ) { ASSERT(!(TmpUserItem->m_dwFlag & USERINFILE)); // // User added and removed // m_UserAddedCnt++; } else if ( TmpUserItem->m_dwFlag & USERINFILE ) { // // User added and removed // m_UserRemovedCnt--; } TmpUserItem->m_dwFlag &= ~USERREMOVED; } // // The caller will count on CUsers to release the memory // for Username and the context if the call is succeeded. This is just for // performance reason. // if (pszUserName) { delete [] pszUserName; } if (pszDnName) { delete [] pszDnName; } if ( pCertContext ) { ::CertFreeCertificateContext (pCertContext); pCertContext = NULL; } return (DWORD) CRYPT_E_EXISTS; } TmpUserItem = TmpUserItem->m_pNext; } try { UserItem = new USERSONFILE; if ( !UserItem ) { AfxThrowMemoryException( ); } UserItem->m_pNext = NULL; // // In case exception raised, we can call delete. // Delete NULL is OK, but random data is not OK. // UserItem->m_UserSid = NULL; UserItem->m_pCert = NULL; UserItem->m_pCertContext = NULL; if ( UserSid ) { SidSize = GetLengthSid (UserSid ); if ( SidSize > 0 ) { UserItem->m_UserSid = new BYTE[SidSize]; if ( !UserItem->m_UserSid ) { AfxThrowMemoryException( ); } if ( !CopySid(SidSize, UserItem->m_UserSid, UserSid)) { delete [] UserItem->m_UserSid; delete UserItem; return GetLastError(); } } else { delete UserItem; return GetLastError(); } } else { UserItem->m_UserSid = NULL; } if ( dwFlag & USERINFILE ) { // // The info is from the file. Use the hash structure // CertHashBlob = ( PEFS_HASH_BLOB ) UserCert; CertSize = sizeof(EFS_HASH_BLOB) + CertHashBlob->cbData; UserItem->m_pCert = new BYTE[CertSize]; if ( !UserItem->m_pCert ) { AfxThrowMemoryException( ); } ((PEFS_HASH_BLOB)UserItem->m_pCert)->cbData = CertHashBlob->cbData; ((PEFS_HASH_BLOB)UserItem->m_pCert)->pbData = (PBYTE)(UserItem->m_pCert) + sizeof(EFS_HASH_BLOB); memcpy(((PEFS_HASH_BLOB)UserItem->m_pCert)->pbData, CertHashBlob->pbData, CertHashBlob->cbData ); } else { // // The info is from the user picked cert. Use m_pCert Blob structure // CertBlob = ( PEFS_CERTIFICATE_BLOB ) UserCert; CertSize = sizeof(EFS_CERTIFICATE_BLOB) + CertBlob->cbData; UserItem->m_pCert = new BYTE[CertSize]; if ( NULL == UserItem->m_pCert ){ AfxThrowMemoryException( ); } ((PEFS_CERTIFICATE_BLOB)UserItem->m_pCert)->cbData = CertBlob->cbData; ((PEFS_CERTIFICATE_BLOB)UserItem->m_pCert)->dwCertEncodingType = CertBlob->dwCertEncodingType; ((PEFS_CERTIFICATE_BLOB)UserItem->m_pCert)->pbData = (PBYTE)(UserItem->m_pCert) + sizeof(EFS_CERTIFICATE_BLOB); memcpy(((PEFS_CERTIFICATE_BLOB)UserItem->m_pCert)->pbData, CertBlob->pbData, CertBlob->cbData ); } UserItem->m_szUserName = pszUserName; UserItem->m_szDnName = pszDnName; UserItem->m_pCertContext = pCertContext; UserItem->m_dwFlag = dwFlag; if ( dwFlag & USERADDED ) { m_UserAddedCnt ++; } } catch (...) { delete [] UserItem->m_UserSid; delete [] UserItem->m_pCert; delete UserItem; AfxThrowMemoryException( ); return ERROR_NOT_ENOUGH_MEMORY; } // // Add to the head // if ( m_UsersRoot ) { UserItem->m_pNext = m_UsersRoot; } m_UsersRoot = UserItem; return NO_ERROR; } DWORD CUsers::Remove( LPCWSTR m_szUserName, LPCWSTR UserCertName ) ////////////////////////////////////////////////////////////////////// // Routine Description: // Remove a user from the list. Actually just mark for remove. // Arguments: // m_szUserName -- User's name // UserCertName -- User's certificate name // Return Value: // NO_ERROR if succeed. // ERROR_NOT_FOUND if the user cannot be found. // ////////////////////////////////////////////////////////////////////// { PUSERSONFILE TmpUserItem = m_UsersRoot; bool bUserMatched = false; while ( TmpUserItem ){ if (((NULL==m_szUserName) && ( NULL == TmpUserItem->m_szUserName)) || ( m_szUserName && TmpUserItem->m_szUserName && !_tcsicmp(m_szUserName, TmpUserItem->m_szUserName))){ // // Make sure the CertName matches also if the user name is NULL // if (NULL==m_szUserName) { if (((NULL==UserCertName) && ( NULL == TmpUserItem->m_szDnName)) || (UserCertName && TmpUserItem->m_szDnName && !_tcsicmp(UserCertName, TmpUserItem->m_szDnName))){ bUserMatched = true; } } else { bUserMatched = true; } if (bUserMatched) { // // User exist, mark it for remove // if ( TmpUserItem->m_dwFlag & USERINFILE ){ m_UserRemovedCnt++; } else if ( TmpUserItem->m_dwFlag & USERADDED ) { m_UserAddedCnt--; } TmpUserItem->m_dwFlag |= USERREMOVED; return NO_ERROR; } } TmpUserItem = TmpUserItem->m_pNext; } return ERROR_NOT_FOUND; } PUSERSONFILE CUsers::StartEnum() ////////////////////////////////////////////////////////////////////// // Routine Description: // Prepare for GetNextUser // Arguments: // // Return Value: // A pointer used for GetNextUser // ////////////////////////////////////////////////////////////////////// { return m_UsersRoot; } PUSERSONFILE CUsers::GetNextUser( PUSERSONFILE Token, CString &szUserName, CString &CertName ) ////////////////////////////////////////////////////////////////////// // Routine Description: // Get next user in the list.(Not removed). // Arguments: // m_szUserName -- m_pNext User's name // CertName -- Certificate name // Token -- A pointer returned by previous GetNextUser or StartEnum. // Return Value: // A pointer for GetNextUser() // ////////////////////////////////////////////////////////////////////// { PUSERSONFILE TmpItem = Token; PUSERSONFILE RetPointer = NULL; while ( TmpItem ) { if ( TmpItem->m_dwFlag & USERREMOVED ) { TmpItem = TmpItem->m_pNext; continue; } try{ szUserName = TmpItem->m_szUserName; CertName = TmpItem->m_szDnName; RetPointer = TmpItem->m_pNext; } catch (...){ // // Out of memory // TmpItem = NULL; RetPointer = NULL; } break; } if ( NULL == TmpItem ) { szUserName.Empty(); CertName.Empty(); } return RetPointer; } DWORD CUsers::GetUserAddedCnt() { return m_UserAddedCnt; } DWORD CUsers::GetUserRemovedCnt() { return m_UserRemovedCnt; } PVOID CUsers::GetNextChangedUser( PVOID Token, LPWSTR * m_szUserName, LPWSTR * m_szDnName, PSID * m_UserSid, PVOID * CertData, DWORD * m_dwFlag ) ////////////////////////////////////////////////////////////////////// // 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 CAddEFSWizSheet (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. // m_szUserName -- User's name // m_szDnName -- User's Distinguished name // CertData -- User's certificate blob or hash // m_UserSid -- User's ID. Can be NULL // m_dwFlag -- Indicate if the item is existing in the file, to be added or removed // Return Value: // m_pNext item pointer. // ////////////////////////////////////////////////////////////////////// { bool bChangedUserFound = false; while ( Token ) { *m_dwFlag = ((PUSERSONFILE) Token)->m_dwFlag; if ( ( *m_dwFlag & USERADDED ) && !( *m_dwFlag & USERREMOVED )) { // // The user is to to be added to the file // *m_dwFlag = USERADDED; bChangedUserFound = true; } else if ( ( *m_dwFlag & USERREMOVED ) && ( *m_dwFlag & USERINFILE)) { // // The user is to be removed from the file // *m_dwFlag = USERREMOVED; bChangedUserFound = true; } if ( bChangedUserFound ) { *m_szUserName = ((PUSERSONFILE) Token)->m_szUserName; *m_szDnName = ((PUSERSONFILE) Token)->m_szDnName; *m_UserSid = ((PUSERSONFILE) Token)->m_UserSid; *CertData = ((PUSERSONFILE) Token)->m_pCert; return ((PUSERSONFILE) Token)->m_pNext; } else { Token = ((PUSERSONFILE) Token)->m_pNext; } } *m_szUserName = NULL; *m_szDnName = NULL; *m_UserSid = NULL; *CertData = NULL; *m_dwFlag = 0; return NULL; } void CUsers::Clear() { PUSERSONFILE TmpUserItem = m_UsersRoot; while (TmpUserItem) { m_UsersRoot = TmpUserItem->m_pNext; delete [] TmpUserItem->m_szUserName; delete [] TmpUserItem->m_szDnName; delete [] TmpUserItem->m_pCert; if (TmpUserItem->m_UserSid) { delete [] TmpUserItem->m_UserSid; } if (TmpUserItem->m_pCertContext) { ::CertFreeCertificateContext(TmpUserItem->m_pCertContext); } delete TmpUserItem; TmpUserItem = m_UsersRoot; } m_UsersRoot = NULL; m_UserAddedCnt = 0; m_UserRemovedCnt = 0; }