/*++ Copyright (c) 1994-1998 Microsoft Corporation Module Name : usrbrows.cpp Abstract: User Browser Dialog Author: Ronald Meijer (ronaldm) Project: Internet Services Manager Revision History: --*/ // // Include Files // #include "stdafx.h" #include #include "comprop.h" #include "objpick.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif extern "C" { #define _NTSEAPI_ // Don't want the security API hdrs #include } #define SZ_USER_CLASS _T("user") #define SZ_GROUP_CLASS _T("group") BOOL CAccessEntry::LookupAccountSid( OUT CString & strFullUserName, OUT int & nPictureID, IN PSID pSid, IN LPCTSTR lpstrSystemName /* OPTIONAL */ ) /*++ Routine Description: Get a full user name and picture ID from the SID Arguments: CString &strFullUserName : Returns the user name int & nPictureID : Returns offset into the imagemap that represents the account type. PSID pSid : Input SID pointer LPCTSTR lpstrSystemName : System name or NULL Return Value: TRUE for success, FALSE for failure. --*/ { DWORD cbUserName = PATHLEN * sizeof(TCHAR); DWORD cbRefDomainName = PATHLEN * sizeof(TCHAR); CString strUserName; CString strRefDomainName; SID_NAME_USE SidToNameUse; LPTSTR lpUserName = strUserName.GetBuffer(PATHLEN); LPTSTR lpRefDomainName = strRefDomainName.GetBuffer(PATHLEN); BOOL fLookUpOK = ::LookupAccountSid( lpstrSystemName, pSid, lpUserName, &cbUserName, lpRefDomainName, &cbRefDomainName, &SidToNameUse ); strUserName.ReleaseBuffer(); strRefDomainName.ReleaseBuffer(); strFullUserName.Empty(); if (fLookUpOK) { if (!strRefDomainName.IsEmpty() && strRefDomainName.CompareNoCase(_T("BUILTIN"))) { strFullUserName += strRefDomainName; strFullUserName += "\\"; } strFullUserName += strUserName; nPictureID = SidToNameUse; } else { strFullUserName.LoadString(IDS_UNKNOWN_USER); nPictureID = SidTypeUnknown; } // // SID_NAME_USE is 1-based // --nPictureID ; return fLookUpOK; } CAccessEntry::CAccessEntry( IN LPVOID pAce, IN BOOL fResolveSID ) /*++ Routine Description: Construct from an ACE Arguments: LPVOID pAce : Pointer to ACE object BOOL fResolveSID : TRUE to resolve the SID immediately Return Value: N/A --*/ : m_pSid(NULL), m_fSIDResolved(FALSE), m_fDeletable(TRUE), m_fInvisible(FALSE), m_nPictureID(SidTypeUnknown-1), // SID_NAME_USE is 1-based m_lpstrSystemName(NULL), m_accMask(0L), m_fDeleted(FALSE), m_strUserName() { MarkEntryAsClean(); PACE_HEADER ph = (PACE_HEADER)pAce; PSID pSID = NULL; switch(ph->AceType) { case ACCESS_ALLOWED_ACE_TYPE: pSID = (PSID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart; m_accMask = ((PACCESS_ALLOWED_ACE)pAce)->Mask; break; case ACCESS_DENIED_ACE_TYPE: case SYSTEM_AUDIT_ACE_TYPE: case SYSTEM_ALARM_ACE_TYPE: default: // // Not supported! // ASSERT(FALSE); break; } if (pSID == NULL) { return; } // // Allocate a new copy of the sid. // DWORD cbSize = ::RtlLengthSid(pSID); m_pSid = (PSID)AllocMem(cbSize); DWORD err = ::RtlCopySid(cbSize, m_pSid, pSID); ASSERT(err == ERROR_SUCCESS); // // Only the non-deletable administrators have execute // privileges // m_fDeletable = (m_accMask & FILE_EXECUTE) == 0L; // // Enum_keys is a special access right that literally "everyone" // has, but it doesn't designate an operator, so it should not // show up in the operator list if this is the only right it has. // m_fInvisible = (m_accMask == MD_ACR_ENUM_KEYS); //SetAccessMask(lpAccessEntry); if (fResolveSID) { ResolveSID(); } } CAccessEntry::CAccessEntry( IN ACCESS_MASK accPermissions, IN PSID pSid, IN LPCTSTR lpstrSystemName, OPTIONAL IN BOOL fResolveSID ) /*++ Routine Description: Constructor from access permissions and SID. Arguments: ACCESS_MASK accPermissions : Access mask PSID pSid : Pointer to SID LPCTSTR lpstrSystemName : Optional system name BOOL fResolveSID : TRUE to resolve SID immediately Return Value: N/A --*/ : m_pSid(NULL), m_fSIDResolved(FALSE), m_fDeletable(TRUE), m_fInvisible(FALSE), m_fDeleted(FALSE), m_nPictureID(SidTypeUnknown-1), // SID_NAME_USE is 1-based m_strUserName(), m_lpstrSystemName(NULL), m_accMask(accPermissions) { MarkEntryAsClean(); // // Allocate a new copy of the sid. // DWORD cbSize = ::RtlLengthSid(pSid); m_pSid = (PSID)AllocMem(cbSize); DWORD err = ::RtlCopySid(cbSize, m_pSid, pSid); ASSERT(err == ERROR_SUCCESS); if (lpstrSystemName != NULL) { m_lpstrSystemName = AllocTString(::lstrlen(lpstrSystemName) + 1); ::lstrcpy(m_lpstrSystemName, lpstrSystemName); } if (fResolveSID) { TRACEEOLID("Bogus SID"); ResolveSID(); } } CAccessEntry::CAccessEntry( IN PSID pSid, IN LPCTSTR pszUserName, IN LPCTSTR pszClassName ) : m_pSid(NULL), m_fSIDResolved(pszUserName != NULL), m_fDeletable(TRUE), m_fInvisible(FALSE), m_fDeleted(FALSE), m_nPictureID(SidTypeUnknown-1), // SID_NAME_USE is 1-based m_strUserName(pszUserName), m_lpstrSystemName(NULL), m_accMask(0) { MarkEntryAsClean(); // // Allocate a new copy of the sid. // DWORD cbSize = ::RtlLengthSid(pSid); m_pSid = (PSID)AllocMem(cbSize); DWORD err = ::RtlCopySid(cbSize, m_pSid, pSid); ASSERT(err == ERROR_SUCCESS); if (lstrcmpi(SZ_USER_CLASS, pszClassName) == 0) m_nPictureID = SidTypeUser - 1; else if (lstrcmpi(SZ_GROUP_CLASS, pszClassName) == 0) m_nPictureID = SidTypeGroup - 1; } CAccessEntry::CAccessEntry( IN CAccessEntry& ae ) : m_fSIDResolved(ae.m_fSIDResolved), m_fDeletable(ae.m_fDeletable), m_fInvisible(ae.m_fInvisible), m_fDeleted(ae.m_fDeleted), m_fDirty(ae.m_fDirty), m_nPictureID(ae.m_nPictureID), m_strUserName(ae.m_strUserName), m_lpstrSystemName(ae.m_lpstrSystemName), m_accMask(ae.m_accMask) { DWORD cbSize = ::RtlLengthSid(ae.m_pSid); m_pSid = (PSID)AllocMem(cbSize); DWORD err = ::RtlCopySid(cbSize, m_pSid, ae.m_pSid); ASSERT(err == ERROR_SUCCESS); } CAccessEntry::~CAccessEntry() /*++ Routine Description: Destructor Arguments: N/A Return Value: N/A --*/ { TRACEEOLID(_T("Destroying local copy of the SID")); ASSERT(m_pSid != NULL); FreeMem(m_pSid); if (m_lpstrSystemName != NULL) { FreeMem(m_lpstrSystemName); } } BOOL CAccessEntry::ResolveSID() /*++ Routine Description: Look up the user name and type. Arguments: None Return Value: TRUE for success, FALSE for failure. Notes: This could take some time. --*/ { // // Even if it fails, it will be considered // resolved // m_fSIDResolved = TRUE; return CAccessEntry::LookupAccountSid( m_strUserName, m_nPictureID, m_pSid, m_lpstrSystemName ); } void CAccessEntry::AddPermissions( IN ACCESS_MASK accNewPermissions ) /*++ Routine Description: Add permissions to this entry. Arguments: ACCESS_MASK accNewPermissions : New access permissions to be added Return Value: None. --*/ { m_accMask |= accNewPermissions; m_fInvisible = (m_accMask == MD_ACR_ENUM_KEYS); m_fDeletable = (m_accMask & FILE_EXECUTE) == 0L; MarkEntryAsChanged(); } void CAccessEntry::RemovePermissions( IN ACCESS_MASK accPermissions ) /*++ Routine Description: Remove permissions from this entry. Arguments: ACCESS_MASK accPermissions : Access permissions to be taken away --*/ { m_accMask &= ~accPermissions; MarkEntryAsChanged(); } // // Helper Functions // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< PSID GetOwnerSID() /*++ Routine Description: Return a pointer to the primary owner SID we're using. Arguments: None Return Value: Pointer to owner SID. --*/ { PSID pSID = NULL; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (!::AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSID )) { TRACEEOLID("Unable to get primary SID " << ::GetLastError()); } return pSID; } BOOL BuildAclBlob( IN CObListPlus & oblSID, OUT CBlob & blob ) /*++ Routine Description: Build a security descriptor blob from the access entries in the oblist Arguments: CObListPlus & oblSID : Input list of access entries CBlob & blob : Output blob Return Value: TRUE if the list is dirty. If the list had no entries marked as dirty, the blob generated will be empty, and this function will return FALSE. Notes: Entries marked as deleted will not be added to the list. --*/ { ASSERT(blob.IsEmpty()); BOOL fAclDirty = FALSE; CAccessEntry * pEntry; DWORD dwAclSize = sizeof(ACL) - sizeof(DWORD); CObListIter obli(oblSID); int cItems = 0; while(pEntry = (CAccessEntry *)obli.Next()) { if (!pEntry->IsDeleted()) { dwAclSize += GetLengthSid(pEntry->GetSid()); dwAclSize += sizeof(ACCESS_ALLOWED_ACE); ++cItems; } if (pEntry->IsDirty()) { fAclDirty = TRUE; } } if (fAclDirty) { // // Build the acl // PACL pacl = NULL; try { if (cItems > 0 && dwAclSize > 0) { pacl = (PACL)AllocMem(dwAclSize); if (InitializeAcl(pacl, dwAclSize, ACL_REVISION)) { obli.Reset(); while(pEntry = (CAccessEntry *)obli.Next()) { if (!pEntry->IsDeleted()) { BOOL f = AddAccessAllowedAce( pacl, ACL_REVISION, pEntry->QueryAccessMask(), pEntry->GetSid() ); ASSERT(f); } } } } // // Build the security descriptor // PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH); VERIFY(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)); VERIFY(SetSecurityDescriptorDacl(pSD, TRUE, pacl, FALSE)); // // Set owner and primary group // PSID pSID = GetOwnerSID(); ASSERT(pSID); VERIFY(SetSecurityDescriptorOwner(pSD, pSID, TRUE)); VERIFY(SetSecurityDescriptorGroup(pSD, pSID, TRUE)); // // Convert to self-relative // PSECURITY_DESCRIPTOR pSDSelfRelative = NULL; DWORD dwSize = 0L; MakeSelfRelativeSD(pSD, pSDSelfRelative, &dwSize); pSDSelfRelative = AllocMem(dwSize); MakeSelfRelativeSD(pSD, pSDSelfRelative, &dwSize); // // Blob takes ownership // blob.SetValue(dwSize, (PBYTE)pSDSelfRelative, FALSE); // // Clean up // FreeMem(pSD); FreeSid(pSID); } catch(CMemoryException * e) { e->ReportError(); e->Delete(); } } return fAclDirty; } DWORD BuildAclOblistFromBlob( IN CBlob & blob, OUT CObListPlus & oblSID ) /*++ Routine Description: Build an oblist of access entries from a security descriptor blob Arguments: CBlob & blob : Input blob CObListPlus & oblSID : Output oblist of access entries Return Value: Error return code --*/ { PSECURITY_DESCRIPTOR pSD = NULL; if (!blob.IsEmpty()) { pSD = (PSECURITY_DESCRIPTOR)blob.GetData(); } if (pSD == NULL) { // // Empty... // return ERROR_SUCCESS; } if (!IsValidSecurityDescriptor(pSD)) { return ::GetLastError(); } ASSERT(GetSecurityDescriptorLength(pSD) == blob.GetSize()); PACL pacl; BOOL fDaclPresent; BOOL fDaclDef; VERIFY(GetSecurityDescriptorDacl(pSD, &fDaclPresent, &pacl, &fDaclDef)); if (!fDaclPresent || pacl == NULL) { return ERROR_SUCCESS; } if (!IsValidAcl(pacl)) { return GetLastError(); } CError err; for (WORD w = 0; w < pacl->AceCount; ++w) { PVOID pAce; if (GetAce(pacl, w, &pAce)) { CAccessEntry * pEntry = new CAccessEntry(pAce, TRUE); oblSID.AddTail(pEntry); } else { // // Save last error, but continue // err.GetLastWinError(); } } // // Return last error // return err; } // // CAccessEntryListBox - a listbox of user SIDs // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< IMPLEMENT_DYNAMIC(CAccessEntryListBox, CRMCListBox); const int CAccessEntryListBox::nBitmaps = 8; void CAccessEntryListBox::DrawItemEx( IN CRMCListBoxDrawStruct& ds ) /*++ Routine Description: Draw item in the listbox Arguments: CRMCListBoxDrawStruct & ds : Input data structure Return Value: N/A --*/ { CAccessEntry * pEntry = (CAccessEntry *)ds.m_ItemData; ASSERT(pEntry != NULL); ASSERT(pEntry->IsSIDResolved()); DrawBitmap(ds, 0, pEntry->QueryPictureID()); ColumnText(ds, 0, TRUE, pEntry->QueryUserName()); } void CAccessEntryListBox::FillAccessListBox( IN CObListPlus & obl ) /*++ Routine Description: Fill a listbox with entries from the oblist. Entries will not be shown if the deleted flag is set, or if their access mask does not fit with the requested access mask. Arguments: CObListPlus & obl : List of access entries Return Value: None. --*/ { CObListIter obli(obl); CAccessEntry * pAccessEntry; // // Remember the selection. // CAccessEntry * pSelEntry = GetSelectedItem(); SetRedraw(FALSE); ResetContent(); int cItems = 0; for ( /**/ ; pAccessEntry = (CAccessEntry *)obli.Next(); ++cItems) { if (pAccessEntry->IsVisible() && !pAccessEntry->IsDeleted()) { AddItem(pAccessEntry); } } SetRedraw(TRUE); SelectItem(pSelEntry); } void CAccessEntryListBox::ResolveAccessList( IN CObListPlus &obl ) /*++ Routine Description: For each member of the list, resolve the SID into a username. Arguments: CObListPlus & obl : List of access entries Return Value: None. --*/ { CObListIter obli(obl); CAccessEntry * pAccessEntry; while (pAccessEntry = (CAccessEntry *)obli.Next()) { if (!pAccessEntry->IsSIDResolved()) { pAccessEntry->ResolveSID(); } } } BOOL CAccessEntryListBox::AddUserPermissions( IN LPCTSTR lpstrServer, IN CObListPlus &oblSID, IN CAccessEntry * newUser, IN ACCESS_MASK accPermissions ) /*++ Routine Description: Add a user to the service list. The return value is the what would be its listbox index. Arguments: LPCTSTR lpstrServer : Server name CObListPlus &oblSID : List of SIDs LPUSERDETAILS pusdtNewUser : User details from user browser ACCESS_MASK accPermissions : Access permissions Return Value: TRUE for success, FALSE for failure. --*/ { CAccessEntry * pAccessEntry; // // Look it up in the list to see if it exists already // CObListIter obli(oblSID); while(pAccessEntry = (CAccessEntry *)obli.Next()) { if (*pAccessEntry == newUser->GetSid()) { TRACEEOLID("Found existing account -- adding permissions"); if (pAccessEntry->IsDeleted()) { // // Back again.. // pAccessEntry->FlagForDeletion(FALSE); } break; } } if (pAccessEntry == NULL) { // I am creating new entry here to be sure that I could delete it from // the input array. SID is copied to new entry. pAccessEntry = new CAccessEntry(*newUser); if (pAccessEntry == NULL) return FALSE; pAccessEntry->MarkEntryAsNew(); oblSID.AddTail(pAccessEntry); } pAccessEntry->AddPermissions(accPermissions); return TRUE; } BOOL CAccessEntryListBox::AddToAccessList( IN CWnd * pWnd, IN LPCTSTR lpstrServer, OUT CObListPlus & obl ) /*++ Routine Description: Bring up the Add Users and Groups dialogs from netui. Arguments: CWnd * pWnd : Parent window LPCTSTR lpstrServer : Server that owns the accounts CObListPlus & obl : Returns the list of selected users. Return Value: TRUE if anything was added, FALSE otherwise. --*/ { CGetUsers usrBrowser(lpstrServer, TRUE); BOOL bRes = usrBrowser.GetUsers(pWnd->GetSafeHwnd()); UINT cItems = 0; if (bRes) { // // Specify access mask for an operator // ACCESS_MASK accPermissions = (MD_ACR_READ | MD_ACR_WRITE | MD_ACR_ENUM_KEYS); for (int i = 0; i < usrBrowser.GetSize(); i++) { if (!AddUserPermissions(lpstrServer, obl, usrBrowser[i], accPermissions)) { bRes = FALSE; break; } cItems++; } } if (cItems > 0) { FillAccessListBox(obl); } return bRes; #if 0 USERBROWSER ub; CError err; // // Specify access mask for an operator // ACCESS_MASK accPermissions = (MD_ACR_READ | MD_ACR_WRITE | MD_ACR_ENUM_KEYS); CString strDomain(lpstrServer); // // Ensure the computer name starts with // backslashes // if (strDomain[0] != _T('\\')) { strDomain = _T("\\\\") + strDomain; } CString strTitle; VERIFY(strTitle.LoadString(IDS_SELECT_ADMIN)); ub.ulStructSize = sizeof(ub); ub.fUserCancelled = FALSE; ub.fExpandNames = FALSE; ub.hwndOwner = pWnd ? pWnd->m_hWnd : NULL; ub.pszTitle = NULL; ub.pszInitialDomain = (LPTSTR)(LPCTSTR)strDomain; ub.Flags = USRBROWS_INCL_EVERYONE | USRBROWS_SHOW_ALL | USRBROWS_EXPAND_USERS; ub.pszHelpFileName = NULL; ub.ulHelpContext = IDHELP_MULT_USRBROWSER; CWinApp * pApp = ::AfxGetApp(); ASSERT(pApp); if (pApp) { ub.pszHelpFileName = (LPTSTR)(LPCTSTR)pApp->m_pszHelpFilePath; } HUSERBROW hUserBrowser = ::OpenUserBrowser(&ub); if (hUserBrowser == NULL) { err.GetLastWinError(); err.MessageBoxOnFailure(MB_OK | MB_ICONHAND); return FALSE; } int cItems = 0; if (!ub.fUserCancelled) { // // Selected users are granted the new privilege // try { // // We break out of this loop ourselves // for ( ;; ) { LPUSERDETAILS pusdtNewUser; DWORD cbSize; // // First call should always fail, but tell // us the size required. // cbSize = 0; EnumUserBrowserSelection(hUserBrowser, NULL, &cbSize); err.GetLastWinError(); if (err.Win32Error() == ERROR_NO_MORE_ITEMS) { // // All done // err.Reset(); break; } if (err.Win32Error() == ERROR_INSUFFICIENT_BUFFER) { err.Reset(); } if (err.Failed()) { break; } // // The EnumUserBrowserSelection API has a bug in which // the size returned might actually by insufficient. // cbSize += 100; TRACEEOLID("Enum structure size requested is " << cbSize); pusdtNewUser = (LPUSERDETAILS)AllocMem(cbSize); if (pusdtNewUser == NULL) { err.GetLastWinError(); break; } if (!EnumUserBrowserSelection( hUserBrowser, pusdtNewUser, &cbSize )) { err.GetLastWinError(); break; } TRACEEOLID("Adding user " << pusdtNewUser->pszAccountName); AddUserPermissions( lpstrServer, obl, pusdtNewUser, accPermissions ); ++cItems; FreeMem(pusdtNewUser); if (err.Failed()) { break; } } } catch(CException * e) { err.GetLastWinError(); TRACEEOLID("Exception generated while enumerating users"); e->Delete(); } } err.MessageBoxOnFailure(); ::CloseUserBrowser(hUserBrowser); if (cItems > 0) { FillAccessListBox(obl); return TRUE; } return FALSE; #endif } static int BrowseCallbackProc( IN HWND hwnd, IN UINT uMsg, IN LPARAM lParam, IN LPARAM lpData ) /*++ Routine Description: Callback function for the folder browser Arguments: hwnd : Handle to the browse dialog box. The callback function can send the following messages to this window: BFFM_ENABLEOK Enables the OK button if the wParam parameter is nonzero or disables it if wParam is zero. BFFM_SETSELECTION Selects the specified folder. The lParam parameter is the PIDL of the folder to select if wParam is FALSE, or it is the path of the folder otherwise. BFFM_SETSTATUSTEXT Sets the status text to the null-terminated string specified by the lParam parameter. uMsg : Value identifying the event. This parameter can be one of the following values: 0 Initialize dir path. lParam is the path. BFFM_INITIALIZED The browse dialog box has finished initializing. lpData is NULL. BFFM_SELCHANGED The selection has changed. lpData is a pointer to the item identifier list for the newly selected folder. lParam : Message-specific value. For more information, see the description of uMsg. lpData : Application-defined value that was specified in the lParam member of the BROWSEINFO structure. Return Value: 0 --*/ { static LPCTSTR lpstrDomain = NULL; switch(uMsg) { case 0: lpstrDomain = (LPCTSTR)lParam; break; case BFFM_INITIALIZED: // // Dialog initialized -- select desired folder // if (lpstrDomain != NULL) { ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpstrDomain); } break; } return 0; } CBrowseDomainDlg::CBrowseDomainDlg( IN CWnd * pParent OPTIONAL, IN LPCTSTR lpszInitialDomain OPTIONAL ) /*++ Routine Description: Constructor for domain browser dialog Arguments: CWnd * pParent : Parent window or NULL LPCTSTR lpszInitialDomain : Initial domain, or NULL Return Value: N/A --*/ : m_strInitialDomain(lpszInitialDomain) { VERIFY(m_strTitle.LoadString(IDS_BROWSE_DOMAIN)); m_bi.pidlRoot = NULL; m_bi.hwndOwner = pParent ? pParent->m_hWnd : NULL; m_bi.pszDisplayName = m_szBuffer; m_bi.lpszTitle = m_strTitle; m_bi.ulFlags = BIF_DONTGOBELOWDOMAIN; m_bi.lpfn = BrowseCallbackProc; m_bi.lParam = 0; // // Let the callback function know the default dir is // lpszInitialDomain = !m_strInitialDomain.IsEmpty() ? (LPCTSTR)m_strInitialDomain : NULL; BrowseCallbackProc(m_bi.hwndOwner, 0, (LPARAM)lpszInitialDomain, NULL); // // Only display network items // LPITEMIDLIST pidl; if (SUCCEEDED(::SHGetSpecialFolderLocation(m_bi.hwndOwner, CSIDL_NETWORK, &pidl))) { m_bi.pidlRoot = pidl; } } CBrowseDomainDlg::~CBrowseDomainDlg() /*++ Routine Description: Destructor for directory browser dialog Arguments: N/A Return Value: N/A --*/ { if (m_bi.pidlRoot != NULL) { LPITEMIDLIST pidl = (LPITEMIDLIST)m_bi.pidlRoot; // // Free using shell allocator // LPMALLOC pMalloc; if (::SHGetMalloc(&pMalloc) == NOERROR) { pMalloc->Free(pidl); pMalloc->Release(); } } } /* virtual */ int CBrowseDomainDlg::DoModal() /*++ Routine Description: Display the browser dialog, and fill in the selected domain. Arguments: None Return Value: IDOK if the OK button was pressed, IDCANCEL otherwise. --*/ { BOOL fSelectionMade = FALSE; // // Gets the Shell's default allocator // LPMALLOC pMalloc; if (::SHGetMalloc(&pMalloc) == NOERROR) { LPITEMIDLIST pidl; if ((pidl = ::SHBrowseForFolder(&m_bi)) != NULL) { // // m_szBuffer contains the selected path already // fSelectionMade = TRUE; // // Free the PIDL allocated by SHBrowseForFolder. // pMalloc->Free(pidl); } // // Release the shell's allocator. // pMalloc->Release(); } return fSelectionMade ? IDOK : IDCANCEL; } LPCTSTR CBrowseDomainDlg::GetSelectedDomain( OUT CString & strName ) const /*++ Routine Description: Get the selected domain Arguments: CString & strName : String in which to return the domain Return Value: A pointer to the domain string or NULL in case of error. Notes: This function should be called only after the dialog has been dismissed. --*/ { LPCTSTR lp = NULL; try { strName = m_szBuffer; lp = strName; } catch(CMemoryException * e) { TRACEEOLID("!!!exception getting path"); e->ReportError(); e->Delete(); strName.Empty(); } return lp; } CBrowseUserDlg::CBrowseUserDlg( IN CWnd * pParentWnd OPTIONAL, IN LPCTSTR lpszTitle, IN LPCTSTR lpszInitialDomain, IN BOOL fExpandNames, IN BOOL fSkipInitialDomainInName, IN DWORD dwFlags, IN LPCTSTR lpszHelpFileName, IN ULONG ulHelpContext ) /*++ Routine Description: Constructor for user browser dialog Arguments: CWnd * pParentWnd : Parent window LPCTSTR lpszTitle : Dialog title text LPCTSTR lpszInitialDomain : Initial domain name BOOL fExpandNames : Expand the names BOOL fSkipInitialDomainInName : Skip the initial domain when expanding DWORD dwFlags : Flags LPCTSTR lpszHelpFileName : Help file path and name ULONG ulHelpContext : Help context ID Return Value: N/A --*/ : m_fSkipInitialDomainInName(fSkipInitialDomainInName), m_strTitle(lpszTitle), m_strInitialDomain(lpszInitialDomain), m_strHelpFileName(lpszHelpFileName) { m_ub.ulStructSize = sizeof(m_ub); m_ub.fExpandNames = fExpandNames; m_ub.hwndOwner = pParentWnd->m_hWnd; m_ub.pszTitle = (LPTSTR)(LPCTSTR)m_strTitle; m_ub.pszInitialDomain = (LPTSTR)(LPCTSTR)m_strInitialDomain; m_ub.Flags = dwFlags; m_ub.pszHelpFileName = (LPTSTR)(LPCTSTR)m_strHelpFileName; m_ub.ulHelpContext = ulHelpContext; } /* virtual */ int CBrowseUserDlg::DoModal() /*++ Routine Description: Show the user browser dialog Arguments: None Return Value: IDOK if the OK button was pressed, IDCANCEL if the dialog was cancelled. --*/ { CError err; m_ub.fUserCancelled = FALSE; m_strSelectedAccounts.RemoveAll(); HUSERBROW hUserBrowser = ::OpenUserBrowser(&m_ub); if (hUserBrowser == NULL) { err.GetLastWinError(); err.MessageBoxOnFailure(MB_OK | MB_ICONHAND); return IDCANCEL; } if (!m_ub.fUserCancelled) { try { CString strInitialDomain(PURE_COMPUTER_NAME(m_ub.pszInitialDomain)); // // We break out of this loop ourselves // for ( ;; ) { LPUSERDETAILS pusdtNewUser; DWORD cbSize; // // First call should always fail, but tell // us the size required. // cbSize = 0; EnumUserBrowserSelection(hUserBrowser, NULL, &cbSize); err.GetLastWinError(); if (err.Win32Error() == ERROR_NO_MORE_ITEMS) { // // All done // err.Reset(); break; } if (err.Win32Error() == ERROR_INSUFFICIENT_BUFFER) { err.Reset(); } if (err.Failed()) { break; } // // The EnumUserBrowserSelection API has a bug in which // the size returned might actually be insufficient. // cbSize += 100; TRACEEOLID("Enum structure size requested is " << cbSize); pusdtNewUser = (LPUSERDETAILS)AllocMem(cbSize); if (pusdtNewUser == NULL) { err.GetLastWinError(); break; } if (!EnumUserBrowserSelection( hUserBrowser, pusdtNewUser, &cbSize )) { err.GetLastWinError(); break; } TRACEEOLID("Adding user " << pusdtNewUser->pszAccountName); CString strAccount; // // If the domain name doesn't match that of the // intial domain, add it as well. // if (!m_fSkipInitialDomainInName || strInitialDomain.CompareNoCase(pusdtNewUser->pszDomainName) != 0) { strAccount += pusdtNewUser->pszDomainName; strAccount += _T('\\'); } strAccount += pusdtNewUser->pszAccountName; m_strSelectedAccounts.AddTail(strAccount); FreeMem(pusdtNewUser); if (err.Failed()) { break; } } } catch(CMemoryException * e) { TRACEEOLID("!!!exception generated while enumerating users"); err = ERROR_NOT_ENOUGH_MEMORY; e->Delete(); } } err.MessageBoxOnFailure(); ::CloseUserBrowser(hUserBrowser); return m_ub.fUserCancelled ? IDCANCEL : IDOK; } // // CUserAccountDlg dialog // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CUserAccountDlg::CUserAccountDlg( IN LPCTSTR lpstrServer, IN LPCTSTR lpstrUserName, IN LPCTSTR lpstrPassword, IN CWnd * pParent OPTIONAL ) /*++ Routine Description: Constructor for bringing up the user account dialog Arguments: LPCTSTR lpstrServer : Server LPCTSTR lpstrUserName : Starting Username LPCTSTR lpstrPassword : Starting Password CWnd * pParent : Parent window handle Return Value: N/A --*/ : m_strUserName(lpstrUserName), m_strPassword(lpstrPassword), m_strServer(lpstrServer), CDialog(CUserAccountDlg::IDD, pParent) { //{{AFX_DATA_INIT(CUserAccountDlg) //}}AFX_DATA_INIT } void CUserAccountDlg::DoDataExchange( IN CDataExchange * pDX ) /*++ Routine Description: Initialise/Store control Data Arguments: CDataExchange * pDX : DDX/DDV struct Return Value: None. --*/ { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CUserAccountDlg) DDX_Control(pDX, IDC_EDIT_USERNAME, m_edit_UserName); DDX_Control(pDX, IDC_EDIT_PASSWORD, m_edit_Password); //}}AFX_DATA_MAP // // Private DDX/DDV Routines // DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUserName); DDV_MinMaxChars(pDX, m_strUserName, 1, UNLEN); DDX_Password(pDX, IDC_EDIT_PASSWORD, m_strPassword, g_lpszDummyPassword); DDV_MaxChars(pDX, m_strPassword, PWLEN); } // // Message Map // BEGIN_MESSAGE_MAP(CUserAccountDlg, CDialog) //{{AFX_MSG_MAP(CUserAccountDlg) ON_BN_CLICKED(IDC_BUTTON_BROWSE_USERS, OnButtonBrowseUsers) ON_BN_CLICKED(IDC_BUTTON_CHECK_PASSWORD, OnButtonCheckPassword) ON_EN_CHANGE(IDC_EDIT_USERNAME, OnChangeEditUsername) //}}AFX_MSG_MAP END_MESSAGE_MAP() // // Message Handlers // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< BOOL GetIUsrAccount( IN LPCTSTR lpstrServer, IN CWnd * pParent, OPTIONAL OUT CString & str ) /*++ Routine Description: Helper function to browse for IUSR Account Arguments: LPCTSTR lpstrServer : Server CWnd * pParent : Parent window CString & str : Will contain the selected account Return Value: TRUE if an account was selected FALSE if not --*/ { CGetUsers usrBrowser(lpstrServer); BOOL bRes = usrBrowser.GetUsers(pParent->GetSafeHwnd(), TRUE); if (bRes) { if (usrBrowser.GetSize() != 0) { str = usrBrowser.GetAt(0)->QueryUserName(); } else bRes = FALSE; } return bRes; #if 0 CString strDomain(lpstrServer); // // Ensure the computer name starts with // backslashes // if (strDomain[0] != _T('\\')) { strDomain = _T("\\\\") + strDomain; } CString strTitle; LPCTSTR lpszHelpPath = NULL; ULONG ulHelpID = IDHELP_USRBROWSER; VERIFY(strTitle.LoadString(IDS_SELECT_IUSR_ACCOUNT)); CWinApp * pApp = ::AfxGetApp(); ASSERT(pApp); if (pApp) { lpszHelpPath = pApp->m_pszHelpFilePath; } CBrowseUserDlg dlg( pParent, strTitle, strDomain, FALSE, TRUE, USRBROWS_EXPAND_USERS | USRBROWS_SINGLE_SELECT | USRBROWS_SHOW_USERS, lpszHelpPath, ulHelpID ); if (dlg.DoModal() == IDOK && dlg.GetSelectionCount() > 0) { str = dlg.GetSelectedAccounts().GetHead(); return TRUE; } return FALSE; #endif } DWORD VerifyUserPassword( IN LPCTSTR lpstrUserName, IN LPCTSTR lpstrPassword ) /*++ Routine Description: Verify the usernamer password combo checks out Arguments: LPCTSTR lpstrUserName : Domain/username combo LPCTSTR lpstrPassword : Password Return Value: ERROR_SUCCESS if the password checks out, an error code otherwise. --*/ { CString strDomain; CString strUser(lpstrUserName); CString strPassword(lpstrPassword); SplitUserNameAndDomain(strUser, strDomain); // // In order to look up an account name, this process // must first be granted the privilege of doing so. // CError err; { HANDLE hToken; LUID AccountLookupValue; TOKEN_PRIVILEGES tkp; do { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ) { err.GetLastWinError(); break; } if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &AccountLookupValue)) { err.GetLastWinError(); break; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = AccountLookupValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL ); err.GetLastWinError(); if (err.Failed()) { break; } HANDLE hUser = NULL; if (LogonUser( strUser.GetBuffer(0), strDomain.GetBuffer(0), strPassword.GetBuffer(0), LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hUser )) { // // Success! // CloseHandle(hUser); } else { err.GetLastWinError(); } // // Remove the privilege // } while(FALSE); } HANDLE hUser = NULL; if (LogonUser( strUser.GetBuffer(0), strDomain.GetBuffer(0), strPassword.GetBuffer(0), LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hUser)) { // // Success! // CloseHandle(hUser); } else { err.GetLastWinError(); } return err; } void CUserAccountDlg::OnButtonBrowseUsers() /*++ Routine Description: User browse dialog pressed, bring up the user browser Arguments: None Return Value: None --*/ { CString str; if (GetIUsrAccount(m_strServer, this, str)) { // // If a name was selected, blank // out the password // m_edit_UserName.SetWindowText(str); m_edit_Password.SetFocus(); } } void CUserAccountDlg::OnChangeEditUsername() /*++ Routine Description: Handle change in user name edit box by blanking out the password Arguments: None Return Value: None --*/ { m_edit_Password.SetWindowText(_T("")); } void CUserAccountDlg::OnButtonCheckPassword() /*++ Routine Description: 'Check Password' has been pressed. Try to validate the password that has been entered Arguments: None Return Value: None --*/ { if (!UpdateData(TRUE)) { return; } CError err(VerifyUserPassword(m_strUserName, m_strPassword)); if (!err.MessageBoxOnFailure()) { ::AfxMessageBox(IDS_PASSWORD_OK); } }