#include "stdafx.h" #include "grpinfo.h" #include "unpage.h" #include "netpage.h" #include "password.h" #include "cryptui.h" // for certificate mgr #pragma hdrstop // Certificate Manager helper static functions and delay-load stuff class CCertificateAPI { public: static BOOL ManagePasswords(HWND hwnd); static BOOL Wizard(HWND hwnd); private: static BOOL m_fFailed; static HINSTANCE m_hInstCryptUI; }; BOOL CCertificateAPI::m_fFailed = FALSE; HINSTANCE CCertificateAPI::m_hInstCryptUI = NULL; // CCertificateAPI::ManagePasswords - launch certificate manager typedef BOOL (WINAPI *PFNCRYPTUIDLGCERTMGR)(IN PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr); BOOL CCertificateAPI::ManagePasswords(HWND hwnd) { // Use shellexecuteex to open up the keyring control panel SHELLEXECUTEINFO shexinfo = {0}; shexinfo.cbSize = sizeof (shexinfo); shexinfo.fMask = SEE_MASK_DOENVSUBST; shexinfo.nShow = SW_SHOWNORMAL; shexinfo.lpFile = L"%windir%\\system32\\rundll32.exe"; shexinfo.lpParameters = L"shell32.dll,Control_RunDLL keymgr.dll"; shexinfo.lpVerb = TEXT("open"); return ShellExecuteEx(&shexinfo); } // CCertificateAPI::Wizard - launch the enrollment wizard typedef BOOL (WINAPI *PFNCRYPTUIWIZCERTREQUEST)(IN DWORD dwFlags, IN OPTIONAL HWND, IN OPTIONAL LPCWSTR pwszWizardTitle, IN PCCRYPTUI_WIZ_CERT_REQUEST_INFO pCertRequestInfo, OUT OPTIONAL PCCERT_CONTEXT *ppCertContext, OUT OPTIONAL DWORD *pCAdwStatus); BOOL CCertificateAPI::Wizard(HWND hwnd) { static PFNCRYPTUIWIZCERTREQUEST pCryptUIWizCertRequest = NULL; if ((m_hInstCryptUI == NULL) && (!m_fFailed)) { m_hInstCryptUI = LoadLibrary(TEXT("cryptui.dll")); } if (m_hInstCryptUI != NULL) { pCryptUIWizCertRequest = (PFNCRYPTUIWIZCERTREQUEST) GetProcAddress(m_hInstCryptUI, "CryptUIWizCertRequest"); } if (pCryptUIWizCertRequest) { CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW CertRequestPvkNew = {0}; CertRequestPvkNew.dwSize=sizeof(CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW); CRYPTUI_WIZ_CERT_REQUEST_INFO CertRequestInfo = {0}; CertRequestInfo.dwSize=sizeof(CRYPTUI_WIZ_CERT_REQUEST_INFO); CertRequestInfo.dwPurpose=CRYPTUI_WIZ_CERT_ENROLL; CertRequestInfo.dwPvkChoice=CRYPTUI_WIZ_CERT_REQUEST_PVK_CHOICE_NEW; CertRequestInfo.pPvkNew=&CertRequestPvkNew; // This can take a while! CWaitCursor cur; pCryptUIWizCertRequest(0, hwnd, NULL, &CertRequestInfo, NULL, NULL); } else { m_fFailed = TRUE; } return (!m_fFailed); } // handle auto logon of users class CAutologonUserDlg: public CDialog { public: CAutologonUserDlg(LPTSTR szInitialUser) {m_pszUsername = szInitialUser;} private: virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); LPTSTR m_pszUsername; }; INT_PTR CAutologonUserDlg::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog); HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand); } return FALSE; } BOOL CAutologonUserDlg::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { HWND hwndUsername = GetDlgItem(hwnd, IDC_USER); HWND hwndPassword = GetDlgItem(hwnd, IDC_PASSWORD); HWND hwndConfirm = GetDlgItem(hwnd, IDC_CONFIRMPASSWORD); // limit the text with of the controls Edit_LimitText(hwndUsername, MAX_USER); Edit_LimitText(hwndPassword, MAX_PASSWORD); Edit_LimitText(hwndConfirm, MAX_PASSWORD); // Populate the username field and set focus to password SetWindowText(hwndUsername, m_pszUsername); SetFocus(hwndPassword); BOOL fSetDefaultFocus = FALSE; return (fSetDefaultFocus); } BOOL CAutologonUserDlg::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDOK: { TCHAR szUsername[MAX_USER + 1]; TCHAR szPassword[MAX_PASSWORD + 1]; TCHAR szConfirm[MAX_PASSWORD + 1]; FetchText(hwnd, IDC_USER, szUsername, ARRAYSIZE(szUsername)); GetWindowText(GetDlgItem(hwnd, IDC_PASSWORD), szPassword, ARRAYSIZE(szPassword)); GetWindowText(GetDlgItem(hwnd, IDC_CONFIRMPASSWORD), szConfirm, ARRAYSIZE(szConfirm)); if (StrCmp(szConfirm, szPassword) != 0) { // Display a message saying the passwords don't match DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_ERR_PWDNOMATCH, MB_OK | MB_ICONERROR); break; } else { // Actually apply the autologon SetAutoLogon(szUsername, szPassword); SecureZeroMemory(szPassword, ARRAYSIZE(szPassword)); } } // Fall through case IDCANCEL: EndDialog(hwnd, id); } return (TRUE); } // user list page (the main page the user sees) class CUserlistPropertyPage: public CPropertyPage { public: CUserlistPropertyPage(CUserManagerData* pdata): m_pData(pdata) {m_himlLarge = NULL;} ~CUserlistPropertyPage(); static HRESULT AddUserToListView(HWND hwndList, CUserInfo* pUserInfo, BOOL fSelectUser = FALSE); private: virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); BOOL OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh); BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); BOOL OnGetInfoTip(HWND hwndList, LPNMLVGETINFOTIP pGetInfoTip); BOOL OnListViewItemChanged(HWND hwnd); BOOL OnListViewDeleteItem(HWND hwndList, int iItem); BOOL OnHelp(HWND hwnd, LPHELPINFO pHelpInfo); BOOL OnContextMenu(HWND hwnd); BOOL OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg); long OnApply(HWND hwnd); HRESULT InitializeListView(HWND hwndList, BOOL fShowDomain); HRESULT LaunchNewUserWizard(HWND hwndParent); HRESULT LaunchAddNetUserWizard(HWND hwndParent); HRESULT LaunchUserProperties(HWND hwndParent); HRESULT LaunchSetPasswordDialog(HWND hwndParent); CUserInfo* GetSelectedUserInfo(HWND hwndList); void OnRemove(HWND hwnd); int ConfirmRemove(HWND hwnd, CUserInfo* pUserInfo); void RemoveSelectedUserFromList(HWND hwndList, BOOL fFreeUserInfo); void SetPageState(HWND hwnd); HRESULT SetAutologonState(HWND hwnd, BOOL fAutologon); void SetupList(HWND hwnd); HPSXA AddExtraUserPropPages(ADDPROPSHEETDATA* ppsd, PSID psid); static int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); CUserManagerData* m_pData; HIMAGELIST m_himlLarge; // When a column header is clicked, the list is sorted based on that column // When this happens, we store the column number here so that if the same // column is clicked again, we sort it in reverse. A 0 is stored if no // column should be sorted in reverse when clicked. int m_iReverseColumnIfSelected; BOOL m_fAutologonCheckChanged; }; // Help ID array static const DWORD rgUserListHelpIds[] = { IDC_AUTOLOGON_CHECK, IDH_AUTOLOGON_CHECK, IDC_LISTTITLE_STATIC, IDH_USER_LIST, IDC_USER_LIST, IDH_USER_LIST, IDC_ADDUSER_BUTTON, IDH_ADDUSER_BUTTON, IDC_REMOVEUSER_BUTTON, IDH_REMOVEUSER_BUTTON, IDC_USERPROPERTIES_BUTTON, IDH_USERPROPERTIES_BUTTON, IDC_PASSWORD_STATIC, IDH_PASSWORD_BUTTON, IDC_CURRENTUSER_ICON, IDH_PASSWORD_BUTTON, IDC_PASSWORD_BUTTON, IDH_PASSWORD_BUTTON, IDC_PWGROUP_STATIC, (DWORD) -1, IDC_ULISTPG_TEXT, (DWORD) -1, IDC_USERLISTPAGE_ICON, (DWORD) -1, 0, 0 }; // Control ID arrays for enabling/disabling/moving static const UINT rgidDisableOnAutologon[] = { IDC_USER_LIST, IDC_ADDUSER_BUTTON, IDC_REMOVEUSER_BUTTON, IDC_USERPROPERTIES_BUTTON, IDC_PASSWORD_BUTTON }; static const UINT rgidDisableOnNoSelection[] = { IDC_REMOVEUSER_BUTTON, IDC_USERPROPERTIES_BUTTON, IDC_PASSWORD_BUTTON }; static const UINT rgidMoveOnNoAutologonCheck[] = { IDC_LISTTITLE_STATIC, IDC_USER_LIST, }; CUserlistPropertyPage::~CUserlistPropertyPage() { if (m_himlLarge != NULL) ImageList_Destroy(m_himlLarge); } INT_PTR CUserlistPropertyPage::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog); HANDLE_MSG(hwndDlg, WM_NOTIFY, OnNotify); HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand); HANDLE_MSG(hwndDlg, WM_SETCURSOR, OnSetCursor); case WM_HELP: return OnHelp(hwndDlg, (LPHELPINFO) lParam); case WM_CONTEXTMENU: return OnContextMenu((HWND) wParam); case WM_ADDUSERTOLIST: return SUCCEEDED(AddUserToListView(GetDlgItem(hwndDlg, IDC_USER_LIST), (CUserInfo*)lParam, (BOOL) wParam)); } return FALSE; } BOOL CUserlistPropertyPage::OnHelp(HWND hwnd, LPHELPINFO pHelpInfo) { WinHelp((HWND) pHelpInfo->hItemHandle, m_pData->GetHelpfilePath(), HELP_WM_HELP, (ULONG_PTR) (LPTSTR)rgUserListHelpIds); return TRUE; } BOOL CUserlistPropertyPage::OnContextMenu(HWND hwnd) { WinHelp(hwnd, m_pData->GetHelpfilePath(), HELP_CONTEXTMENU, (ULONG_PTR) (LPTSTR)rgUserListHelpIds); return TRUE; } BOOL CUserlistPropertyPage::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST); InitializeListView(hwndList, m_pData->IsComputerInDomain()); m_pData->Initialize(hwnd); SetupList(hwnd); m_fAutologonCheckChanged = FALSE; return TRUE; } BOOL CUserlistPropertyPage::OnListViewDeleteItem(HWND hwndList, int iItem) { LVITEM lvi = {0}; lvi.iItem = iItem; lvi.mask = LVIF_PARAM; ListView_GetItem(hwndList, &lvi); CUserInfo* pUserInfo = (CUserInfo*)lvi.lParam; if (NULL != pUserInfo) { delete pUserInfo; } return TRUE; } BOOL CUserlistPropertyPage::OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh) { switch (pnmh->code) { case PSN_APPLY: { long applyEffect = OnApply(hwnd); SetWindowLongPtr(hwnd, DWLP_MSGRESULT, applyEffect); break; } case LVN_GETINFOTIP: return OnGetInfoTip(pnmh->hwndFrom, (LPNMLVGETINFOTIP) pnmh); break; case LVN_ITEMCHANGED: return OnListViewItemChanged(hwnd); break; case LVN_DELETEITEM: return OnListViewDeleteItem(GetDlgItem(hwnd, IDC_USER_LIST), ((LPNMLISTVIEW) pnmh)->iItem); case NM_DBLCLK: LaunchUserProperties(hwnd); return TRUE; case LVN_COLUMNCLICK: { int iColumn = ((LPNMLISTVIEW) pnmh)->iSubItem; // Want to work with 1-based columns so we can use zero as // a special value iColumn += 1; // If we aren't showing the domain column because we're in // non-domain mode, then map column 2 (group since we're not in // domain mode to column 3 since the callback always expects // the columns to be, "username", "domain", "group". if ((iColumn == 2) && (!m_pData->IsComputerInDomain())) { iColumn = 3; } if (m_iReverseColumnIfSelected == iColumn) { m_iReverseColumnIfSelected = 0; iColumn = -iColumn; } else { m_iReverseColumnIfSelected = iColumn; } ListView_SortItems(pnmh->hwndFrom, ListCompare, (LPARAM) iColumn); return TRUE; } default: return FALSE; } return TRUE; } BOOL CUserlistPropertyPage::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDC_ADDUSER_BUTTON: if (m_pData->IsComputerInDomain()) { // Launch the wizard to add a network user to a local group LaunchAddNetUserWizard(hwnd); } else { // No domain; create a new local machine user LaunchNewUserWizard(hwnd); } return TRUE; case IDC_REMOVEUSER_BUTTON: OnRemove(hwnd); return TRUE; case IDC_AUTOLOGON_CHECK: { m_fAutologonCheckChanged = TRUE; BOOL fAutoLogon = (BST_UNCHECKED == SendMessage(GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK), BM_GETCHECK, 0, 0)); SetAutologonState(hwnd, fAutoLogon); SetPageState(hwnd); break; } case IDC_ADVANCED_BUTTON: { static const TCHAR szMMCCommandLineFormat[] = TEXT("mmc.exe /computer=%s %%systemroot%%\\system32\\lusrmgr.msc"); TCHAR szMMCCommandLine[MAX_PATH]; TCHAR szExpandedCommandLine[MAX_PATH]; wnsprintf(szMMCCommandLine, ARRAYSIZE(szMMCCommandLine), szMMCCommandLineFormat, m_pData->GetComputerName()); if (ExpandEnvironmentStrings(szMMCCommandLine, szExpandedCommandLine, ARRAYSIZE(szExpandedCommandLine)) > 0) { PROCESS_INFORMATION pi; STARTUPINFO si = {0}; si.cb = sizeof (si); if (CreateProcess(NULL, szExpandedCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } break; } case IDC_PASSWORD_BUTTON: LaunchSetPasswordDialog(hwnd); break; case IDC_USERPROPERTIES_BUTTON: LaunchUserProperties(hwnd); break; } return FALSE; } BOOL CUserlistPropertyPage::OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg) { BOOL fHandled = FALSE; if (m_pData->GetUserListLoader()->InitInProgress()) { // If the thread is filling, handle by setting the appstarting cursor SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); fHandled = TRUE; } SetWindowLongPtr(hwnd, DWLP_MSGRESULT, fHandled); return TRUE; } BOOL CUserlistPropertyPage::OnGetInfoTip(HWND hwndList, LPNMLVGETINFOTIP pGetInfoTip) { // Get the UserInfo structure for the selected item LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iItem = pGetInfoTip->iItem; lvi.iSubItem = 0; if ((lvi.iItem >= 0) && (ListView_GetItem(hwndList, &lvi))) { // Ensure full name and comment are available CUserInfo* pUserInfo = (CUserInfo*)lvi.lParam; pUserInfo->GetExtraUserInfo(); // Make a string containing our "Full Name: %s\nComment: %s" message if ((pUserInfo->m_szFullName[0] != TEXT('\0')) && (pUserInfo->m_szComment[0] != TEXT('\0'))) { // We have a full name and comment FormatMessageString(IDS_USR_TOOLTIPBOTH_FORMAT, pGetInfoTip->pszText, pGetInfoTip->cchTextMax, pUserInfo->m_szFullName, pUserInfo->m_szComment); } else if (pUserInfo->m_szFullName[0] != TEXT('\0')) { // We only have full name FormatMessageString(IDS_USR_TOOLTIPFULLNAME_FORMAT, pGetInfoTip->pszText, pGetInfoTip->cchTextMax, pUserInfo->m_szFullName); } else if (pUserInfo->m_szComment[0] != TEXT('\0')) { // We only have comment FormatMessageString(IDS_USR_TOOLTIPCOMMENT_FORMAT, pGetInfoTip->pszText, pGetInfoTip->cchTextMax, pUserInfo->m_szComment); } else { // We have no extra information - do nothing (show no tip) } } return TRUE; } struct MYCOLINFO { int percentWidth; UINT idString; }; HRESULT CUserlistPropertyPage::InitializeListView(HWND hwndList, BOOL fShowDomain) { // Array of icon ids icons 0, 1, and 2 respectively static const UINT rgIcons[] = { IDI_USR_LOCALUSER_ICON, IDI_USR_DOMAINUSER_ICON, IDI_USR_GROUP_ICON }; // Array of relative column widths, for columns 0, 1, and 2 respectively static const MYCOLINFO rgColWidthsWithDomain[] = { {40, IDS_USR_NAME_COLUMN}, {30, IDS_USR_DOMAIN_COLUMN}, {30, IDS_USR_GROUP_COLUMN} }; static const MYCOLINFO rgColWidthsNoDomain[] = { {50, IDS_USR_NAME_COLUMN}, {50, IDS_USR_GROUP_COLUMN} }; // Create a listview with three columns RECT rect; GetClientRect(hwndList, &rect); // Width of our window minus width of a verticle scroll bar minus one for the // little bevel at the far right of the header. int cxListView = (rect.right - rect.left) - GetSystemMetrics(SM_CXVSCROLL) - 1; // Make our columns int i; int nColumns; const MYCOLINFO* pColInfo; if (fShowDomain) { nColumns = ARRAYSIZE(rgColWidthsWithDomain); pColInfo = rgColWidthsWithDomain; } else { nColumns = ARRAYSIZE(rgColWidthsNoDomain); pColInfo = rgColWidthsNoDomain; } LVCOLUMN lvc; lvc.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH; for (i = 0; i < nColumns; i++) { TCHAR szText[MAX_PATH]; // Load this column's caption LoadString(g_hinst, pColInfo[i].idString, szText, ARRAYSIZE(szText)); lvc.iSubItem = i; lvc.cx = (int) MulDiv(pColInfo[i].percentWidth, cxListView, 100); lvc.pszText = szText; ListView_InsertColumn(hwndList, i, &lvc); } UINT flags = ILC_MASK; if(IS_WINDOW_RTL_MIRRORED(hwndList)) { flags |= ILC_MIRROR; } // Create an image list for the listview HIMAGELIST himlSmall = ImageList_Create(16, 16, flags, 0, ARRAYSIZE(rgIcons)); // Large image lists for the "set password" group icon m_himlLarge = ImageList_Create(32, 32, flags, 0, ARRAYSIZE(rgIcons)); if (himlSmall && m_himlLarge) { // Add our icons to the image list for(i = 0; i < ARRAYSIZE(rgIcons); i ++) { HICON hIconSmall = (HICON) LoadImage(g_hinst, MAKEINTRESOURCE(rgIcons[i]), IMAGE_ICON, 16, 16, 0); if (hIconSmall) { ImageList_AddIcon(himlSmall, hIconSmall); DestroyIcon(hIconSmall); } HICON hIconLarge = (HICON) LoadImage(g_hinst, MAKEINTRESOURCE(rgIcons[i]), IMAGE_ICON, 32, 32, 0); if (hIconLarge) { ImageList_AddIcon(m_himlLarge, hIconLarge); DestroyIcon(hIconLarge); } } } ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL); // Set extended styles for the listview ListView_SetExtendedListViewStyleEx(hwndList, LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); // Set some settings for our tooltips - stolen from defview.cpp code HWND hwndInfoTip = ListView_GetToolTips(hwndList); if (hwndInfoTip != NULL) { //make the tooltip window to be topmost window SetWindowPos(hwndInfoTip, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); // increase the ShowTime (the delay before we show the tooltip) to 2 times the default value LRESULT uiShowTime = SendMessage(hwndInfoTip, TTM_GETDELAYTIME, TTDT_INITIAL, 0); SendMessage(hwndInfoTip, TTM_SETDELAYTIME, TTDT_INITIAL, uiShowTime * 2); } return S_OK; } HRESULT CUserlistPropertyPage::AddUserToListView(HWND hwndList, CUserInfo* pUserInfo, BOOL fSelectUser /* = FALSE */) { if (!pUserInfo->m_fAccountDisabled) { LVITEM lvi = { 0 }; lvi.iItem = 0; lvi.iSubItem = 0; lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; lvi.pszText = pUserInfo->m_szUsername; lvi.iImage = pUserInfo->m_userType; lvi.lParam = (LPARAM) pUserInfo; // Always select the first loaded user if (ListView_GetItemCount(hwndList) == 0) fSelectUser = TRUE; if (fSelectUser) { lvi.mask |= LVIF_STATE; lvi.state = lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED; } int iItem = ListView_InsertItem(hwndList, &lvi); if (iItem >= 0) { if (fSelectUser) ListView_EnsureVisible(hwndList, iItem, FALSE); // Make the item visible // Success! Now add the subitems (domain, groups) lvi.iItem = iItem; lvi.mask = LVIF_TEXT; // Only add the domain field if the user is in a domain if (::IsComputerInDomain()) { lvi.iSubItem = 1; lvi.pszText = pUserInfo->m_szDomain; ListView_SetItem(hwndList, &lvi); // User is in a domain; group should be third column lvi.iSubItem = 2; } else { // User isn't in a domain, group should be second column lvi.iSubItem = 1; } // Add group regardless of whether user is in a domain lvi.pszText = pUserInfo->m_szGroups; ListView_SetItem(hwndList, &lvi); } } /* else { // Do we leak the pUserInfo for disabled user accounts? } */ return S_OK; } HRESULT CUserlistPropertyPage::LaunchNewUserWizard(HWND hwndParent) { static const int nPages = 3; int cPages = 0; HPROPSHEETPAGE rghPages[nPages]; // Create a new user record CUserInfo* pNewUser = new CUserInfo; if ( !pNewUser ) { DisplayFormatMessage(hwndParent, IDS_USR_NEWUSERWIZARD_CAPTION, IDS_USR_CREATE_MISC_ERROR, MB_OK | MB_ICONERROR); return E_OUTOFMEMORY; } pNewUser->InitializeForNewUser(); pNewUser->m_userType = CUserInfo::LOCALUSER; PROPSHEETPAGE psp = {0}; // Common propsheetpage settings psp.dwSize = sizeof (psp); psp.hInstance = g_hinst; psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER; // Page 1: Username entry page psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERNAME_WIZARD_PAGE); CUsernameWizardPage page1(pNewUser); page1.SetPropSheetPageMembers(&psp); rghPages[cPages++] = CreatePropertySheetPage(&psp); // Page 2: Password page psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_PASSWORD_WIZARD_PAGE); CPasswordWizardPage page2(pNewUser); page2.SetPropSheetPageMembers(&psp); rghPages[cPages++] = CreatePropertySheetPage(&psp); // Page 3: Local group addition psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_WIZARD_PAGE); CGroupWizardPage page3(pNewUser, m_pData->GetGroupList()); page3.SetPropSheetPageMembers(&psp); rghPages[cPages++] = CreatePropertySheetPage(&psp); PROPSHEETHEADER psh = {0}; psh.dwSize = sizeof (psh); psh.dwFlags = PSH_NOCONTEXTHELP | PSH_WIZARD | PSH_WIZARD_LITE; psh.hwndParent = hwndParent; psh.hInstance = g_hinst; psh.nPages = nPages; psh.phpage = rghPages; if (PropertySheet(&psh) == IDOK) { AddUserToListView(GetDlgItem(hwndParent, IDC_USER_LIST), pNewUser, TRUE); } else { // User clicked cancel delete pNewUser; pNewUser = NULL; } return S_OK; } HRESULT CUserlistPropertyPage::LaunchAddNetUserWizard(HWND hwndParent) { HRESULT hr = S_OK; static const int nPages = 2; int cPages = 0; HPROPSHEETPAGE rghPages[nPages]; // Create a new user record CUserInfo* pNewUser = new CUserInfo; if ( !pNewUser ) { DisplayFormatMessage(hwndParent, IDS_USR_NEWUSERWIZARD_CAPTION, IDS_USR_CREATE_MISC_ERROR, MB_OK | MB_ICONERROR); return E_OUTOFMEMORY; } pNewUser->InitializeForNewUser(); pNewUser->m_userType = CUserInfo::DOMAINUSER; PROPSHEETPAGE psp = {0}; // Common propsheetpage settings psp.dwSize = sizeof (psp); psp.hInstance = g_hinst; psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER; // Page 1: Find a network user page psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_FINDNETUSER_WIZARD_PAGE); CNetworkUserWizardPage page1(pNewUser); page1.SetPropSheetPageMembers(&psp); rghPages[cPages++] = CreatePropertySheetPage(&psp); // Page 2: Local group addition psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_WIZARD_PAGE); CGroupWizardPage page2(pNewUser, m_pData->GetGroupList()); page2.SetPropSheetPageMembers(&psp); rghPages[cPages++] = CreatePropertySheetPage(&psp); PROPSHEETHEADER psh = {0}; psh.dwSize = sizeof (psh); psh.dwFlags = PSH_NOCONTEXTHELP | PSH_WIZARD | PSH_WIZARD_LITE; psh.hwndParent = hwndParent; psh.hInstance = g_hinst; psh.nPages = nPages; psh.phpage = rghPages; if (PropertySheet(&psh) == IDOK) { AddUserToListView(GetDlgItem(hwndParent, IDC_USER_LIST), pNewUser, TRUE); m_pData->UserInfoChanged(pNewUser->m_szUsername, pNewUser->m_szDomain); } else { // No errors, but the user clicked Cancel... delete pNewUser; pNewUser = NULL; } return S_OK; } HRESULT CUserlistPropertyPage::LaunchUserProperties(HWND hwndParent) { HRESULT hr = S_OK; HWND hwndList = GetDlgItem(hwndParent, IDC_USER_LIST); CUserInfo* pUserInfo = GetSelectedUserInfo(hwndList); if (pUserInfo != NULL) { pUserInfo->GetExtraUserInfo(); // page addition information ADDPROPSHEETDATA apsd; apsd.nPages = 0; // Common propsheetpage settings PROPSHEETPAGE psp = {0}; psp.dwSize = sizeof (psp); psp.hInstance = g_hinst; psp.dwFlags = PSP_DEFAULT; // If we have a local user, show both the username and group page, ow // just the group page // Page 1: Username entry page psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERNAME_PROP_PAGE); CUsernamePropertyPage page1(pUserInfo); page1.SetPropSheetPageMembers(&psp); // Only actually create the prop page if we have a local user if (pUserInfo->m_userType == CUserInfo::LOCALUSER) { apsd.rgPages[apsd.nPages++] = CreatePropertySheetPage(&psp); } // Always add the second page // Page 2: Local group addition psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_PROP_PAGE); CGroupPropertyPage page2(pUserInfo, m_pData->GetGroupList()); page2.SetPropSheetPageMembers(&psp); apsd.rgPages[apsd.nPages++] = CreatePropertySheetPage(&psp); HPSXA hpsxa = AddExtraUserPropPages(&apsd, pUserInfo->m_psid); PROPSHEETHEADER psh = {0}; psh.dwSize = sizeof (psh); psh.dwFlags = PSH_DEFAULT | PSH_PROPTITLE; TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2]; MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser)); psh.pszCaption = szDomainUser; psh.hwndParent = hwndParent; psh.hInstance = g_hinst; psh.nPages = apsd.nPages; psh.phpage = apsd.rgPages; int iRetCode = (int)PropertySheet(&psh); if (hpsxa != NULL) SHDestroyPropSheetExtArray(hpsxa); if (iRetCode == IDOK) { // PropSheet_Changed(GetParent(hwndParent), hwndParent); // So that we don't delete this pUserInfo when we remove // this user from the list: m_pData->UserInfoChanged(pUserInfo->m_szUsername, (pUserInfo->m_szDomain[0] == 0) ? NULL : pUserInfo->m_szDomain); RemoveSelectedUserFromList(hwndList, FALSE); AddUserToListView(hwndList, pUserInfo, TRUE); } } return S_OK; } CUserInfo* CUserlistPropertyPage::GetSelectedUserInfo(HWND hwndList) { int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); if (iItem >= 0) { LVITEM lvi = {0}; lvi.mask = LVIF_PARAM; lvi.iItem = iItem; if (ListView_GetItem(hwndList, &lvi)) { return (CUserInfo*)lvi.lParam; } } return NULL; } void CUserlistPropertyPage::RemoveSelectedUserFromList(HWND hwndList, BOOL fFreeUserInfo) { int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); // If we don't want to delete this user info, better set it to NULL if (!fFreeUserInfo) { LVITEM lvi = {0}; lvi.iItem = iItem; lvi.mask = LVIF_PARAM; lvi.lParam = (LPARAM) (CUserInfo*) NULL; ListView_SetItem(hwndList, &lvi); } ListView_DeleteItem(hwndList, iItem); int iSelect = iItem > 0 ? iItem - 1 : 0; ListView_SetItemState(hwndList, iSelect, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); SetFocus(hwndList); } void CUserlistPropertyPage::OnRemove(HWND hwnd) { HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST); CUserInfo* pUserInfo = GetSelectedUserInfo(hwndList); if (pUserInfo) { if (ConfirmRemove(hwnd, pUserInfo) == IDYES) { if (SUCCEEDED(pUserInfo->Remove())) { RemoveSelectedUserFromList(hwndList, TRUE); } else { TCHAR szDisplayName[MAX_USER + MAX_DOMAIN + 2]; ::MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDisplayName, ARRAYSIZE(szDisplayName)); DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_USR_REMOVE_MISC_ERROR, MB_ICONERROR | MB_OK, szDisplayName); } } } } int CUserlistPropertyPage::ConfirmRemove(HWND hwnd, CUserInfo* pUserInfo) { TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2]; MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser)); return DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_USR_REMOVEUSER_WARNING, MB_ICONEXCLAMATION | MB_YESNO, szDomainUser); } void CUserlistPropertyPage::SetPageState(HWND hwnd) { BOOL fAutologon = (BST_UNCHECKED == SendMessage(GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK), BM_GETCHECK, 0, 0)); EnableControls(hwnd, rgidDisableOnAutologon, ARRAYSIZE(rgidDisableOnAutologon), !fAutologon); HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST); CUserInfo* pUserInfo = GetSelectedUserInfo(hwndList); if (pUserInfo) { TCHAR szPWGroup[128]; FormatMessageString(IDS_USR_PWGROUP_FORMAT, szPWGroup, ARRAYSIZE(szPWGroup), pUserInfo->m_szUsername); SetWindowText(GetDlgItem(hwnd, IDC_PWGROUP_STATIC), szPWGroup); TCHAR szPWMessage[128]; // If the logged on user is the selected user CUserInfo* pLoggedOnUser = m_pData->GetLoggedOnUserInfo(); if ((StrCmpI(pUserInfo->m_szUsername, pLoggedOnUser->m_szUsername) == 0) && (StrCmpI(pUserInfo->m_szDomain, pLoggedOnUser->m_szDomain) == 0)) { LoadString(g_hinst, IDS_USR_YOURPWMESSAGE_FORMAT, szPWMessage, ARRAYSIZE(szPWMessage)); EnableWindow(GetDlgItem(hwnd, IDC_PASSWORD_BUTTON), FALSE); } // If the user is a local user else if (pUserInfo->m_userType == CUserInfo::LOCALUSER) { // We can set this user's password FormatMessageString(IDS_USR_PWMESSAGE_FORMAT, szPWMessage, ARRAYSIZE(szPWMessage), pUserInfo->m_szUsername); } else { // Nothing can be done with this user's password // the selected user may be a domain user or a group or something // We can set this user's password FormatMessageString(IDS_USR_CANTCHANGEPW_FORMAT, szPWMessage, ARRAYSIZE(szPWMessage), pUserInfo->m_szUsername); EnableWindow(GetDlgItem(hwnd, IDC_PASSWORD_BUTTON), FALSE); } SetWindowText(GetDlgItem(hwnd, IDC_PASSWORD_STATIC), szPWMessage); // Set the icon for the user HICON hIcon = ImageList_GetIcon(m_himlLarge, pUserInfo->m_userType, ILD_NORMAL); Static_SetIcon(GetDlgItem(hwnd, IDC_CURRENTUSER_ICON), hIcon); } else { EnableControls(hwnd, rgidDisableOnNoSelection, ARRAYSIZE(rgidDisableOnNoSelection), FALSE); } } HRESULT CUserlistPropertyPage::SetAutologonState(HWND hwnd, BOOL fAutologon) { PropSheet_Changed(GetParent(hwnd), hwnd); return S_OK; } BOOL CUserlistPropertyPage::OnListViewItemChanged(HWND hwnd) { SetPageState(hwnd); return TRUE; } long CUserlistPropertyPage::OnApply(HWND hwnd) { long applyEffect = PSNRET_NOERROR; BOOL fAutologonSet = (BST_UNCHECKED == SendMessage(GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK), BM_GETCHECK, 0, 0)); if (!fAutologonSet) { ClearAutoLogon(); // Ensure autologon is cleared } else if (m_fAutologonCheckChanged) { CUserInfo* pSelectedUser = GetSelectedUserInfo(GetDlgItem(hwnd, IDC_USER_LIST)); TCHAR szNullName[] = TEXT(""); CAutologonUserDlg dlg((pSelectedUser != NULL) ? pSelectedUser->m_szUsername : szNullName); if (dlg.DoModal(g_hinst, MAKEINTRESOURCE(IDD_USR_AUTOLOGON_DLG), hwnd) == IDCANCEL) { applyEffect = PSNRET_INVALID_NOCHANGEPAGE; } } m_fAutologonCheckChanged = FALSE; if (applyEffect == PSNRET_INVALID_NOCHANGEPAGE) { m_pData->Initialize(hwnd); // Reload the data and list SetupList(hwnd); } return applyEffect; } void CUserlistPropertyPage::SetupList(HWND hwnd) { HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST); // Disable autologon check box in the domain case where autologon isn't // enabled HWND hwndCheck = GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK); if (m_pData->IsComputerInDomain() && !m_pData->IsAutologonEnabled()) { ShowWindow(hwndCheck, SW_HIDE); EnableWindow(hwndCheck, FALSE); // Move most controls up a bit if the autologon is not visible RECT rcBottom; GetWindowRect(GetDlgItem(hwnd, IDC_LISTTITLE_STATIC), &rcBottom); RECT rcTop; GetWindowRect(hwndCheck, &rcTop); int dy = rcTop.top - rcBottom.top; OffsetControls(hwnd, rgidMoveOnNoAutologonCheck, ARRAYSIZE(rgidMoveOnNoAutologonCheck), 0, dy); // Grow the list by this amount also RECT rcList; GetWindowRect(hwndList, &rcList); SetWindowPos(hwndList, NULL, 0, 0, rcList.right - rcList.left, rcList.bottom - rcList.top - dy, SWP_NOZORDER|SWP_NOMOVE); } SendMessage(hwndCheck, BM_SETCHECK, m_pData->IsAutologonEnabled() ? BST_UNCHECKED : BST_CHECKED, 0); // Set the text in the set password group. SetPageState(hwnd); } HRESULT CUserlistPropertyPage::LaunchSetPasswordDialog(HWND hwndParent) { CUserInfo* pUserInfo = GetSelectedUserInfo(GetDlgItem(hwndParent, IDC_USER_LIST)); if ((pUserInfo != NULL) && (pUserInfo->m_userType == CUserInfo::LOCALUSER)) { CChangePasswordDlg dlg(pUserInfo); dlg.DoModal(g_hinst, MAKEINTRESOURCE(IDD_USR_SETPASSWORD_DLG), hwndParent); return S_OK; } return E_FAIL; } #define MAX_EXTRA_PAGES 10 HPSXA CUserlistPropertyPage::AddExtraUserPropPages(ADDPROPSHEETDATA* ppsd, PSID psid) { HPSXA hpsxa = NULL; IDataObject *pdo; HRESULT hr = CUserSidDataObject_CreateInstance(psid, &pdo); if (SUCCEEDED(hr)) { hpsxa = SHCreatePropSheetExtArrayEx(HKEY_LOCAL_MACHINE, REGSTR_USERPROPERTIES_SHEET, MAX_EXTRA_PAGES, pdo); if (hpsxa) { SHAddFromPropSheetExtArray(hpsxa, AddPropSheetPageCallback, (LPARAM) ppsd); } pdo->Release(); } return hpsxa; } // ListCompare // Compares list items in for sorting the listview by column // lParamSort gets the 1-based column index. If lParamSort is negative // it indicates that the given column should be sorted in reverse. int CUserlistPropertyPage::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { CUserInfo* pUserInfo1 = (CUserInfo*)lParam1; CUserInfo* pUserInfo2 = (CUserInfo*)lParam2; int iColumn = (int)lParamSort; BOOL fReverse = FALSE; if (iColumn < 0) { fReverse = TRUE; iColumn = -iColumn; } int iResult = 0; // they match... switch (iColumn) { case 1: iResult = lstrcmpi(pUserInfo1->m_szUsername, pUserInfo2->m_szUsername); break; case 2: iResult = lstrcmpi(pUserInfo1->m_szDomain, pUserInfo2->m_szDomain); break; case 3: iResult = lstrcmpi(pUserInfo1->m_szGroups, pUserInfo2->m_szGroups); break; } if (fReverse) iResult = -iResult; return iResult; } // The DoModal call for this dialog will return IDOK if the applet should be // shown or IDCANCEL if the users.cpl should exit without displaying the applet. // dsheldon - TODO: Remove this dialog and don't let non-admins runas the cpl class CSecurityCheckDlg: public CDialog { public: CSecurityCheckDlg(LPCTSTR pszDomainUser): m_pszDomainUser(pszDomainUser) {} private: virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); BOOL OnNotify(HWND hwnd, int id, NMHDR* pnmhdr); HRESULT RelaunchAsUser(HWND hwnd); LPCTSTR m_pszDomainUser; }; // implementation INT_PTR CSecurityCheckDlg::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog); HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand); HANDLE_MSG(hwndDlg, WM_NOTIFY, OnNotify); } return FALSE; } BOOL CSecurityCheckDlg::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { // First we must check if the current user is a local administrator; if this is // the case, our dialog doesn't even display BOOL fIsLocalAdmin; if (SUCCEEDED(IsUserLocalAdmin(NULL, &fIsLocalAdmin))) { if (fIsLocalAdmin) { EndDialog(hwnd, IDOK); // We want to continue and launch the applet (don't display the security check dlg) } } else { EndDialog(hwnd, IDCANCEL); } // Set the "can't launch User Options" message TCHAR szUsername[MAX_USER + 1]; DWORD cchUsername = ARRAYSIZE(szUsername); TCHAR szDomain[MAX_DOMAIN + 1]; DWORD cchDomain = ARRAYSIZE(szDomain); if (GetCurrentUserAndDomainName(szUsername, &cchUsername, szDomain, &cchDomain)) { TCHAR szDomainAndUsername[MAX_DOMAIN + MAX_USER + 2]; MakeDomainUserString(szDomain, szUsername, szDomainAndUsername, ARRAYSIZE(szDomainAndUsername)); TCHAR szMessage[256]; if (FormatMessageString(IDS_USR_CANTRUNCPL_FORMAT, szMessage, ARRAYSIZE(szMessage), szDomainAndUsername)) { SetWindowText(GetDlgItem(hwnd, IDC_CANTRUNCPL_STATIC), szMessage); } TCHAR szAdministrator[MAX_USER + 1]; LoadString(g_hinst, IDS_ADMINISTRATOR, szAdministrator, ARRAYSIZE(szAdministrator)); SetWindowText(GetDlgItem(hwnd, IDC_USER), szAdministrator); TCHAR szMachine[MAX_COMPUTERNAME + 1]; DWORD dwSize = ARRAYSIZE(szMachine); ::GetComputerName(szMachine, &dwSize); SetWindowText(GetDlgItem(hwnd, IDC_DOMAIN), szMachine); } // Limit the text in the edit fields HWND hwndUsername = GetDlgItem(hwnd, IDC_USER); Edit_LimitText(hwndUsername, MAX_USER); HWND hwndDomain = GetDlgItem(hwnd, IDC_DOMAIN); Edit_LimitText(hwndDomain, MAX_DOMAIN); HWND hwndPassword = GetDlgItem(hwnd, IDC_PASSWORD); Edit_LimitText(hwndPassword, MAX_PASSWORD); if (!IsComputerInDomain()) { // Don't need domain box EnableWindow(hwndDomain, FALSE); ShowWindow(hwndDomain, SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_DOMAIN_STATIC), SW_HIDE); // Move up the OK/Cancel buttons and text and shrink the dialog RECT rcDomain; GetWindowRect(hwndDomain, &rcDomain); RECT rcPassword; GetWindowRect(hwndPassword, &rcPassword); int dy = (rcPassword.top - rcDomain.top); // dy is negative OffsetWindow(GetDlgItem(hwnd, IDOK), 0, dy); OffsetWindow(GetDlgItem(hwnd, IDCANCEL), 0, dy); OffsetWindow(GetDlgItem(hwnd, IDC_PASSWORD_STATIC), 0, dy); OffsetWindow(GetDlgItem(hwnd, IDC_OTHEROPTIONS_LINK), 0, dy); RECT rcDialog; GetWindowRect(hwnd, &rcDialog); rcDialog.bottom += dy; MoveWindow(hwnd, rcDialog.left, rcDialog.top, rcDialog.right-rcDialog.left, rcDialog.bottom-rcDialog.top, FALSE); } return TRUE; } BOOL CSecurityCheckDlg::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDOK: if (SUCCEEDED(RelaunchAsUser(hwnd))) { EndDialog(hwnd, IDCANCEL); } return TRUE; case IDCANCEL: EndDialog(hwnd, IDCANCEL); return TRUE; } return FALSE; } BOOL CSecurityCheckDlg::OnNotify(HWND hwnd, int id, NMHDR *pnmhdr) { BOOL fHandled = FALSE; switch (pnmhdr->code) { // Handle link window clicks case NM_CLICK: case NM_RETURN: { if (IDC_OTHEROPTIONS_LINK == id) { // First link in the control is "manage passwords", second is "passport wizard" NMLINKWND* pnm = (NMLINKWND*) pnmhdr; if (0 == pnm->item.iLink) { // Launch "manage passwords" CCertificateAPI::ManagePasswords(hwnd); } else if (1 == pnm->item.iLink) { // Launch passport wizard IPassportWizard *pPW; if (SUCCEEDED(CoCreateInstance(CLSID_PassportWizard, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPassportWizard, &pPW)))) { pPW->SetOptions(PPW_LAUNCHEDBYUSER); pPW->Show(hwnd); pPW->Release(); } } fHandled = TRUE; } } break; }; return fHandled; } HRESULT CSecurityCheckDlg::RelaunchAsUser(HWND hwnd) { USES_CONVERSION; HRESULT hr = E_FAIL; TCHAR szUsername[MAX_USER + 1]; FetchText(hwnd, IDC_USER, szUsername, ARRAYSIZE(szUsername)); TCHAR szDomain[MAX_DOMAIN + 1]; FetchText(hwnd, IDC_DOMAIN, szDomain, ARRAYSIZE(szDomain)); // If the user didn't type a domain if (szDomain[0] == TEXT('\0')) { // Use this machine as the domain DWORD cchComputername = ARRAYSIZE(szDomain); ::GetComputerName(szDomain, &cchComputername); } TCHAR szPassword[MAX_PASSWORD + 1]; GetWindowText(GetDlgItem(hwnd, IDC_PASSWORD), szPassword, ARRAYSIZE(szPassword)); // Now relaunch ourselves with this information STARTUPINFO startupinfo = {0}; startupinfo.cb = sizeof (startupinfo); WCHAR c_szCommandLineFormat[] = L"rundll32.exe netplwiz.dll,UsersRunDll %s"; // Put the "real" user name in the command-line so that we know what user is // actually logged on to the machine even though we are re-launching in a different // user context WCHAR szCommandLine[ARRAYSIZE(c_szCommandLineFormat) + MAX_DOMAIN + MAX_USER + 2]; wnsprintf(szCommandLine, ARRAYSIZE(szCommandLine), c_szCommandLineFormat, m_pszDomainUser); PROCESS_INFORMATION process_information; if (CreateProcessWithLogonW(szUsername, szDomain, szPassword, LOGON_WITH_PROFILE, NULL, szCommandLine, 0, NULL, NULL, &startupinfo, &process_information)) { CloseHandle(process_information.hProcess); CloseHandle(process_information.hThread); hr = S_OK; } else { DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_USR_CANTOPENCPLASUSER_ERROR, MB_OK|MB_ICONERROR); } return hr; } // Advanced Property Page class CAdvancedPropertyPage: public CPropertyPage { public: CAdvancedPropertyPage(CUserManagerData* pdata): m_pData(pdata), m_fRebootRequired(FALSE) {} private: virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); BOOL OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh); BOOL OnHelp(HWND hwnd, LPHELPINFO pHelpInfo); BOOL OnContextMenu(HWND hwnd); void ReadRequireCad(BOOL* pfRequireCad, BOOL* pfSetInPolicy); void WriteRequireCad(BOOL fRequireCad); CUserManagerData* m_pData; BOOL m_fRebootRequired; }; // Relevant regkeys/regvals #define REGKEY_WINLOGON \ TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") #define REGKEY_WINLOGON_POLICY \ TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System") #define REGVAL_DISABLE_CAD TEXT("DisableCAD") void CAdvancedPropertyPage::ReadRequireCad(BOOL* pfRequireCad, BOOL* pfSetInPolicy) { HKEY hkey; DWORD dwSize; DWORD dwType; BOOL fDisableCad; NT_PRODUCT_TYPE nttype; *pfRequireCad = TRUE; *pfSetInPolicy = FALSE; if (!RtlGetNtProductType(&nttype)) { nttype = NtProductWinNt; } // By default, don't require CAD for workstations not // on a domain only if ((NtProductWinNt == nttype) && !IsComputerInDomain()) { *pfRequireCad = FALSE; } // Read the setting from the machine preferences if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WINLOGON, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(fDisableCad); if (ERROR_SUCCESS == RegQueryValueEx (hkey, REGVAL_DISABLE_CAD, NULL, &dwType, (LPBYTE) &fDisableCad, &dwSize)) { *pfRequireCad = !fDisableCad; } RegCloseKey (hkey); } // Check if C-A-D is disabled via policy if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WINLOGON_POLICY, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(fDisableCad); if (ERROR_SUCCESS == RegQueryValueEx (hkey, REGVAL_DISABLE_CAD, NULL, &dwType, (LPBYTE) &fDisableCad, &dwSize)) { *pfRequireCad = !fDisableCad; *pfSetInPolicy = TRUE; } RegCloseKey (hkey); } } void CAdvancedPropertyPage::WriteRequireCad(BOOL fRequireCad) { HKEY hkey; DWORD dwDisp; BOOL fDisableCad = !fRequireCad; if (ERROR_SUCCESS == RegCreateKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WINLOGON, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &dwDisp)) { RegSetValueEx(hkey, REGVAL_DISABLE_CAD, 0, REG_DWORD, (LPBYTE) &fDisableCad, sizeof(fDisableCad)); RegCloseKey (hkey); } } static const DWORD rgAdvHelpIds[] = { IDC_ADVANCED_BUTTON, IDH_ADVANCED_BUTTON, IDC_BOOT_ICON, IDH_SECUREBOOT_CHECK, IDC_BOOT_TEXT, IDH_SECUREBOOT_CHECK, IDC_REQUIRECAD, IDH_SECUREBOOT_CHECK, IDC_MANAGEPWD_BUTTON, IDH_MANAGEPWD_BUTTON, IDC_PASSPORTWIZARD, IDH_PASSPORTWIZARD, IDC_CERT_ICON, (DWORD) -1, IDC_CERT_TEXT, (DWORD) -1, 0, 0 }; INT_PTR CAdvancedPropertyPage::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog); HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand); HANDLE_MSG(hwndDlg, WM_NOTIFY, OnNotify); case WM_HELP: return OnHelp(hwndDlg, (LPHELPINFO) lParam); case WM_CONTEXTMENU: return OnContextMenu((HWND) wParam); } return FALSE; } BOOL CAdvancedPropertyPage::OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh) { BOOL fReturn = FALSE; switch (pnmh->code) { case PSN_APPLY: { HWND hwndCheck = GetDlgItem(hwnd, IDC_REQUIRECAD); BOOL fRequireCad = (BST_CHECKED == Button_GetCheck(hwndCheck)); // See if a change is really necessary BOOL fOldRequireCad; BOOL fDummy; ReadRequireCad(&fOldRequireCad, &fDummy); if (fRequireCad != fOldRequireCad) { WriteRequireCad(fRequireCad); // m_fRebootRequired = TRUE; // Uncomment the line above if it ever becomes necessary to reboot the machine - it isn't now. } // xxx->lParam == 0 means Ok as opposed to Apply if ((((PSHNOTIFY*) pnmh)->lParam) && m_fRebootRequired) { PropSheet_RebootSystem(GetParent(hwnd)); } SetWindowLongPtr(hwnd, DWLP_MSGRESULT, PSNRET_NOERROR); fReturn = TRUE; } break; } return fReturn; } BOOL CAdvancedPropertyPage::OnHelp(HWND hwnd, LPHELPINFO pHelpInfo) { WinHelp((HWND) pHelpInfo->hItemHandle, m_pData->GetHelpfilePath(), HELP_WM_HELP, (ULONG_PTR) (LPTSTR)rgAdvHelpIds); return TRUE; } BOOL CAdvancedPropertyPage::OnContextMenu(HWND hwnd) { WinHelp(hwnd, m_pData->GetHelpfilePath(), HELP_CONTEXTMENU, (ULONG_PTR) (LPTSTR)rgAdvHelpIds); return TRUE; } BOOL CAdvancedPropertyPage::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { // Do the required mucking for the Require C-A-D checkbox... // Read the setting for the require CAD checkbox BOOL fRequireCad; BOOL fSetInPolicy; ReadRequireCad(&fRequireCad, &fSetInPolicy); HWND hwndCheck = GetDlgItem(hwnd, IDC_REQUIRECAD); // Disable the check if set in policy EnableWindow(hwndCheck, !fSetInPolicy); // Set the check accordingly Button_SetCheck(hwndCheck, fRequireCad ? BST_CHECKED : BST_UNCHECKED); return TRUE; } BOOL CAdvancedPropertyPage::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDC_MANAGEPWD_BUTTON: { CCertificateAPI::ManagePasswords(hwnd); } break; case IDC_PASSPORTWIZARD: { IPassportWizard *pPW; if (SUCCEEDED(CoCreateInstance(CLSID_PassportWizard, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPassportWizard, &pPW)))) { pPW->SetOptions(PPW_LAUNCHEDBYUSER); pPW->Show(hwnd); pPW->Release(); } } break; case IDC_ADVANCED_BUTTON: { // Launch the MMC local user manager STARTUPINFO startupinfo = {0}; startupinfo.cb = sizeof (startupinfo); PROCESS_INFORMATION process_information; static const TCHAR szMMCCommandLine[] = TEXT("mmc.exe %systemroot%\\system32\\lusrmgr.msc computername=localmachine"); TCHAR szExpandedCommandLine[MAX_PATH]; if (ExpandEnvironmentStrings(szMMCCommandLine, szExpandedCommandLine, ARRAYSIZE(szExpandedCommandLine)) > 0) { if (CreateProcess(NULL, szExpandedCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupinfo, &process_information)) { CloseHandle(process_information.hProcess); CloseHandle(process_information.hThread); } } } break; case IDC_REQUIRECAD: PropSheet_Changed(GetParent(hwnd), hwnd); break; } return FALSE; } // users control panel entry point void APIENTRY UsersRunDll(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow) { HRESULT hr = S_OK; TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2]; *szDomainUser = 0; // Get the "real" user of this machine - this may be passed in the cmdline if (0 == *pszCmdLine) { // user wasn't passed, assume its the currently logged on user TCHAR szUser[MAX_USER + 1]; DWORD cchUser = ARRAYSIZE(szUser); TCHAR szDomain[MAX_DOMAIN + 1]; DWORD cchDomain = ARRAYSIZE(szDomain); if (0 != GetCurrentUserAndDomainName(szUser, &cchUser, szDomain, &cchDomain)) { MakeDomainUserString(szDomain, szUser, szDomainUser, ARRAYSIZE(szDomainUser)); } } else { // User name was passed in, just copy it MultiByteToWideChar(GetACP(), 0, pszCmdLine, -1, szDomainUser, ARRAYSIZE(szDomainUser)); } // Initialize COM, but continue even if this fails. BOOL fComInited = SUCCEEDED(CoInitialize(NULL)); // See if we're already running TCHAR szCaption[256]; LoadString(g_hinst, IDS_USR_APPLET_CAPTION, szCaption, ARRAYSIZE(szCaption)); CEnsureSingleInstance ESI(szCaption); if (!ESI.ShouldExit()) { LinkWindow_RegisterClass(); // Create the security check dialog to ensure the logged-on user // is a local admin CSecurityCheckDlg dlg(szDomainUser); if (dlg.DoModal(g_hinst, MAKEINTRESOURCE(IDD_USR_SECURITYCHECK_DLG), NULL) == IDOK) { // Create the shared user mgr object CUserManagerData data(szDomainUser); // Create the property sheet and page template // Maximum number of pages ADDPROPSHEETDATA ppd; ppd.nPages = 0; // Settings common to all pages PROPSHEETPAGE psp = {0}; psp.dwSize = sizeof (psp); psp.hInstance = g_hinst; // Create the userlist property sheet page and its managing object psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERLIST_PAGE); CUserlistPropertyPage userListPage(&data); userListPage.SetPropSheetPageMembers(&psp); ppd.rgPages[ppd.nPages++] = CreatePropertySheetPage(&psp); psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_ADVANCED_PAGE); CAdvancedPropertyPage advancedPage(&data); advancedPage.SetPropSheetPageMembers(&psp); ppd.rgPages[ppd.nPages++] = CreatePropertySheetPage(&psp); HPSXA hpsxa = SHCreatePropSheetExtArrayEx(HKEY_LOCAL_MACHINE, REGSTR_USERSANDPASSWORDS_CPL, 10, NULL); if (hpsxa != NULL) SHAddFromPropSheetExtArray(hpsxa, AddPropSheetPageCallback, (LPARAM)&ppd); // Create the prop sheet PROPSHEETHEADER psh = {0}; psh.dwSize = sizeof (psh); psh.dwFlags = PSH_DEFAULT; psh.hwndParent = hwndStub; psh.hInstance = g_hinst; psh.pszCaption = szCaption; psh.nPages = ppd.nPages; psh.phpage = ppd.rgPages; // Show the property sheet int iRetCode = PropertySheetIcon(&psh, MAKEINTRESOURCE(IDI_USR_USERS)); if (hpsxa != NULL) { SHDestroyPropSheetExtArray(hpsxa); } if (iRetCode == -1) { hr = E_FAIL; } else { hr = S_OK; // Special case when we must restart or reboot if (iRetCode == ID_PSREBOOTSYSTEM) { RestartDialogEx(NULL, NULL, EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG); } else if (iRetCode == ID_PSRESTARTWINDOWS) { RestartDialogEx(NULL, NULL, EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG); } else if (data.LogoffRequired()) { int iLogoff = DisplayFormatMessage(NULL, IDS_USERSANDPASSWORDS, IDS_LOGOFFREQUIRED, MB_YESNO | MB_ICONQUESTION); if (iLogoff == IDYES) { // Tell explorer to log off the "real" logged on user. We need to do this // since they may be running U&P as a different user. HWND hwnd = FindWindow(TEXT("Shell_TrayWnd"), TEXT("")); if ( hwnd ) { UINT uMsg = RegisterWindowMessage(TEXT("Logoff User")); PostMessage(hwnd, uMsg, 0,0); } } } } } else { // Security check told us to exit; either another instance of the CPL is starting // with admin priviledges or the user cancelled on the sec. check. dlg. hr = E_FAIL; } } if (fComInited) CoUninitialize(); } // user property property page object class CUserPropertyPages: public IShellExtInit, IShellPropSheetExt { public: CUserPropertyPages(); ~CUserPropertyPages(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IShellExtInit STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pdo, HKEY hkeyProgID); // IShellPropSheetExt STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam); STDMETHODIMP ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam) { return E_NOTIMPL; } private: LONG _cRef; CUserInfo *_pUserInfo; // The user for the property sheet CUsernamePropertyPage *_pUserNamePage; // Basic info page, only shown for local users CGroupPropertyPage *_pGroupPage; // The group page, which is common to both local and domain users CGroupInfoList _GroupList; // The group list, used by the group page }; CUserPropertyPages::CUserPropertyPages() : _cRef(1) { DllAddRef(); } CUserPropertyPages::~CUserPropertyPages() { if (_pUserInfo) delete _pUserInfo; if (_pUserNamePage) delete _pUserNamePage; if (_pGroupPage) delete _pGroupPage; DllRelease(); } ULONG CUserPropertyPages::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CUserPropertyPages::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CUserPropertyPages::QueryInterface(REFIID riid, LPVOID* ppv) { static const QITAB qit[] = { QITABENT(CUserPropertyPages, IShellExtInit), // IID_IShellExtInit QITABENT(CUserPropertyPages, IShellPropSheetExt), // IID_IShellPropSheetExt {0, 0 }, }; return QISearch(this, qit, riid, ppv); } // IShellExtInit HRESULT CUserPropertyPages::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pdo, HKEY hkeyProgID) { // Request the user's SID from the data object FORMATETC fmt = {0}; fmt.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_USERPROPPAGESSID); fmt.dwAspect = DVASPECT_CONTENT; fmt.lindex = -1; fmt.tymed = TYMED_HGLOBAL; STGMEDIUM medium = { 0 }; HRESULT hr = pdo->GetData(&fmt, &medium); if (SUCCEEDED(hr)) { // medium.hGlobal is the user's SID; make sure it isn't null and that // we haven't already set our copy of the SID if ((medium.hGlobal != NULL) && (_pUserInfo == NULL)) { PSID psid = (PSID) GlobalLock(medium.hGlobal); if (IsValidSid(psid)) { // Create a user info structure to party on _pUserInfo = new CUserInfo; if (_pUserInfo) { hr = _pUserInfo->Load(psid, TRUE); if (SUCCEEDED(hr)) { hr = _GroupList.Initialize(); // Get the groups } } else { hr = E_OUTOFMEMORY; } } else { hr = E_INVALIDARG; } GlobalUnlock(medium.hGlobal); } else { hr = E_UNEXPECTED; // hGlobal was NULL or prop sheet was already init'ed } ReleaseStgMedium(&medium); } return hr; } // AddPages - handles adding the property pages HRESULT CUserPropertyPages::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) { PROPSHEETPAGE psp = {0}; psp.dwSize = sizeof (psp); psp.hInstance = g_hinst; if (_pUserInfo->m_userType == CUserInfo::LOCALUSER) { // Add the local user prop pages psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERNAME_PROP_PAGE); _pUserNamePage = new CUsernamePropertyPage(_pUserInfo); if (_pUserNamePage != NULL) { _pUserNamePage->SetPropSheetPageMembers(&psp); lpfnAddPage(CreatePropertySheetPage(&psp), lParam); } } psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_PROP_PAGE); _pGroupPage = new CGroupPropertyPage(_pUserInfo, &_GroupList); if (_pGroupPage != NULL) { _pGroupPage->SetPropSheetPageMembers(&psp); lpfnAddPage(CreatePropertySheetPage(&psp), lParam); } return S_OK; } STDAPI CUserPropertyPages_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CUserPropertyPages *pupp = new CUserPropertyPages(); if (!pupp) { *ppunk = NULL; // incase of failure return E_OUTOFMEMORY; } HRESULT hr = pupp->QueryInterface(IID_PPV_ARG(IUnknown, ppunk)); pupp->Release(); return hr; } // expose the SID for a user via an IDataObject class CUserSidDataObject: public IDataObject { public: CUserSidDataObject(); ~CUserSidDataObject(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IDataObject STDMETHODIMP GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium); STDMETHODIMP GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) { return E_NOTIMPL; } STDMETHODIMP QueryGetData(FORMATETC* pFormatEtc) { return E_NOTIMPL; } STDMETHODIMP GetCanonicalFormatEtc(FORMATETC* pFormatetcIn, FORMATETC* pFormatetcOut) { return E_NOTIMPL; } STDMETHODIMP SetData(FORMATETC* pFormatetc, STGMEDIUM* pmedium, BOOL fRelease) { return E_NOTIMPL; } STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC ** ppenumFormatetc) { return E_NOTIMPL; } STDMETHODIMP DAdvise(FORMATETC* pFormatetc, DWORD advf, IAdviseSink* pAdvSink, DWORD * pdwConnection) { return E_NOTIMPL; } STDMETHODIMP DUnadvise(DWORD dwConnection) { return E_NOTIMPL; } STDMETHODIMP EnumDAdvise(IEnumSTATDATA ** ppenumAdvise) { return E_NOTIMPL; } HRESULT SetSid(PSID psid); private: LONG _cRef; PSID _psid; }; CUserSidDataObject::CUserSidDataObject() : _cRef(1) { DllAddRef(); } CUserSidDataObject::~CUserSidDataObject() { if (_psid) LocalFree(_psid); DllRelease(); } ULONG CUserSidDataObject::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CUserSidDataObject::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CUserSidDataObject::QueryInterface(REFIID riid, LPVOID* ppv) { static const QITAB qit[] = { QITABENT(CUserSidDataObject, IDataObject), // IID_IDataObject {0, 0 }, }; return QISearch(this, qit, riid, ppv); } HRESULT CUserSidDataObject::GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium) { HRESULT hr = QueryGetData(pFormatEtc); if (SUCCEEDED(hr)) { pMedium->pUnkForRelease = (IDataObject*)this; AddRef(); // reference to ourself pMedium->tymed = TYMED_HGLOBAL; pMedium->hGlobal = (HGLOBAL)_psid; } return hr; } HRESULT CUserSidDataObject::SetSid(PSID psid) { if (!psid) return E_INVALIDARG; if (_psid == NULL) { DWORD cbSid = GetLengthSid(psid); _psid = (PSID)LocalAlloc(0, cbSid); if (!_psid) return E_OUTOFMEMORY; if (CopySid(cbSid, _psid, psid)) return S_OK; } return E_FAIL; } STDAPI CUserSidDataObject_CreateInstance(PSID psid, IDataObject **ppdo) { CUserSidDataObject *pusdo = new CUserSidDataObject(); if (!pusdo) return E_OUTOFMEMORY; HRESULT hr = pusdo->SetSid(psid); if (SUCCEEDED(hr)) { hr = pusdo->QueryInterface(IID_PPV_ARG(IDataObject, ppdo)); } pusdo->Release(); return hr; }