/* private static bool ShowLangBar(bool bShow) * { * try * { * return KpLibNativeMethods.TfShowLangBar(bShow ? * KpLibNativeMethods.TF_SFT_SHOWNORMAL : * KpLibNativeMethods.TF_SFT_HIDDEN); * } * catch(Exception) { } * * return false; * } */ private static void HandleUnexpectedDesktopSwitch(IntPtr pOrgDesktop, IntPtr pNewDesktop, SecureThreadInfo stp) { NativeMethods.SwitchDesktop(pOrgDesktop); NativeMethods.SetThreadDesktop(pOrgDesktop); ProcessMessagesEx(); // Do not use MessageService.ShowWarning, because // it uses the top form's thread MessageService.ExternalIncrementMessageCount(); NativeMethods.MessageBoxFlags mbf = (NativeMethods.MessageBoxFlags.MB_ICONWARNING | NativeMethods.MessageBoxFlags.MB_TASKMODAL | NativeMethods.MessageBoxFlags.MB_SETFOREGROUND | NativeMethods.MessageBoxFlags.MB_TOPMOST); if (StrUtil.RightToLeft) { mbf |= (NativeMethods.MessageBoxFlags.MB_RTLREADING | NativeMethods.MessageBoxFlags.MB_RIGHT); } NativeMethods.MessageBox(IntPtr.Zero, KPRes.SecDeskOtherSwitched + MessageService.NewParagraph + KPRes.SecDeskSwitchBack, PwDefs.ShortProductName, mbf); MessageService.ExternalDecrementMessageCount(); SecureThreadState st; lock (stp) { st = stp.State; } if (st != SecureThreadState.Terminated) { NativeMethods.SwitchDesktop(pNewDesktop); ProcessMessagesEx(); } }
public DialogResult ShowDialog(out object objResult, object objConstructParam) { objResult = null; ProcessMessagesEx(); // Creating a window on the new desktop spawns a CtfMon.exe child // process by default. On Windows Vista, this process is terminated // correctly when the desktop is closed. However, on Windows 7 it // isn't terminated (probably a bug); creating multiple desktops // accumulates CtfMon.exe child processes. ChildProcessesSnapshot cpsCtfMons = new ChildProcessesSnapshot( "CtfMon.exe"); ClipboardEventChainBlocker ccb = new ClipboardEventChainBlocker(); byte[] pbClipHash = ClipboardUtil.ComputeHash(); SecureThreadInfo stp = new SecureThreadInfo(); foreach (Screen sc in Screen.AllScreens) { Bitmap bmpBack = UIUtil.CreateScreenshot(sc); if (bmpBack != null) { UIUtil.DimImage(bmpBack); } stp.BackgroundBitmaps.Add(bmpBack); } DialogResult dr = DialogResult.None; try { uint uOrgThreadId = NativeMethods.GetCurrentThreadId(); IntPtr pOrgDesktop = NativeMethods.GetThreadDesktop(uOrgThreadId); string strName = "D" + Convert.ToBase64String( CryptoRandom.Instance.GetRandomBytes(16), Base64FormattingOptions.None); strName = strName.Replace(@"+", string.Empty); strName = strName.Replace(@"/", string.Empty); strName = strName.Replace(@"=", string.Empty); if (strName.Length > 15) { strName = strName.Substring(0, 15); } NativeMethods.DesktopFlags deskFlags = (NativeMethods.DesktopFlags.CreateMenu | NativeMethods.DesktopFlags.CreateWindow | NativeMethods.DesktopFlags.ReadObjects | NativeMethods.DesktopFlags.WriteObjects | NativeMethods.DesktopFlags.SwitchDesktop); IntPtr pNewDesktop = NativeMethods.CreateDesktop(strName, null, IntPtr.Zero, 0, deskFlags, IntPtr.Zero); if (pNewDesktop == IntPtr.Zero) { throw new InvalidOperationException(); } bool bNameSupported = NativeMethods.DesktopNameContains(pNewDesktop, strName).GetValueOrDefault(false); Debug.Assert(bNameSupported); stp.ThreadDesktop = pNewDesktop; stp.FormConstructParam = objConstructParam; Thread th = new Thread(this.SecureDialogThread); th.CurrentCulture = Thread.CurrentThread.CurrentCulture; th.CurrentUICulture = Thread.CurrentThread.CurrentUICulture; th.Start(stp); SecureThreadState st = SecureThreadState.None; while (st != SecureThreadState.Terminated) { th.Join(150); lock (stp) { st = stp.State; } if ((st == SecureThreadState.ShowingDialog) && bNameSupported) { IntPtr hCurDesk = NativeMethods.OpenInputDesktop(0, false, NativeMethods.DesktopFlags.ReadObjects); if (hCurDesk == IntPtr.Zero) { Debug.Assert(false); continue; } if (hCurDesk == pNewDesktop) { if (!NativeMethods.CloseDesktop(hCurDesk)) { Debug.Assert(false); } continue; } bool?obOnSec = NativeMethods.DesktopNameContains(hCurDesk, strName); if (!NativeMethods.CloseDesktop(hCurDesk)) { Debug.Assert(false); } lock (stp) { st = stp.State; } // Update; might have changed if (obOnSec.HasValue && !obOnSec.Value && (st == SecureThreadState.ShowingDialog)) { HandleUnexpectedDesktopSwitch(pOrgDesktop, pNewDesktop, stp); } } } if (!NativeMethods.SwitchDesktop(pOrgDesktop)) { Debug.Assert(false); } NativeMethods.SetThreadDesktop(pOrgDesktop); th.Join(); // Ensure thread terminated before closing desktop if (!NativeMethods.CloseDesktop(pNewDesktop)) { Debug.Assert(false); } NativeMethods.CloseDesktop(pOrgDesktop); // Optional dr = stp.DialogResult; objResult = stp.ResultObject; } catch (Exception) { Debug.Assert(false); } byte[] pbNewClipHash = ClipboardUtil.ComputeHash(); if ((pbClipHash != null) && (pbNewClipHash != null) && !MemUtil.ArraysEqual(pbClipHash, pbNewClipHash)) { ClipboardUtil.Clear(); } ccb.Dispose(); foreach (Bitmap bmpBack in stp.BackgroundBitmaps) { if (bmpBack != null) { bmpBack.Dispose(); } } stp.BackgroundBitmaps.Clear(); cpsCtfMons.TerminateNewChildsAsync(4100); // If something failed, show the dialog on the normal desktop if (dr == DialogResult.None) { Form f = m_fnConstruct(objConstructParam); dr = f.ShowDialog(); objResult = m_fnResultBuilder(f); UIUtil.DestroyForm(f); } return(dr); }
private void SecureDialogThread(object oParam) { SecureThreadInfo stp = (oParam as SecureThreadInfo); if (stp == null) { Debug.Assert(false); return; } List <BackgroundForm> lBackForms = new List <BackgroundForm>(); BackgroundForm formBackPrimary = null; // bool bLangBar = false; try { if (!NativeMethods.SetThreadDesktop(stp.ThreadDesktop)) { Debug.Assert(false); return; } ProcessMessagesEx(); // Test whether we're really on the secure desktop if (NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId()) != stp.ThreadDesktop) { Debug.Assert(false); return; } // Disabling IME is not required anymore; we terminate // CtfMon.exe child processes manually // try { NativeMethods.ImmDisableIME(0); } // Always false on 2000/XP // catch(Exception) { Debug.Assert(!WinUtil.IsAtLeastWindows2000); } ProcessMessagesEx(); Screen[] vScreens = Screen.AllScreens; Screen scPrimary = Screen.PrimaryScreen; Debug.Assert(vScreens.Length == stp.BackgroundBitmaps.Count); int sMin = Math.Min(vScreens.Length, stp.BackgroundBitmaps.Count); for (int i = sMin - 1; i >= 0; --i) { Bitmap bmpBack = stp.BackgroundBitmaps[i]; if (bmpBack == null) { continue; } Debug.Assert(bmpBack.Size == vScreens[i].Bounds.Size); BackgroundForm formBack = new BackgroundForm(bmpBack, vScreens[i]); lBackForms.Add(formBack); if (vScreens[i].Equals(scPrimary)) { formBackPrimary = formBack; } formBack.Show(); } if (formBackPrimary == null) { Debug.Assert(false); if (lBackForms.Count > 0) { formBackPrimary = lBackForms[lBackForms.Count - 1]; } } ProcessMessagesEx(); if (!NativeMethods.SwitchDesktop(stp.ThreadDesktop)) { Debug.Assert(false); } ProcessMessagesEx(); Form f = m_fnConstruct(stp.FormConstructParam); if (f == null) { Debug.Assert(false); return; } if (Program.Config.UI.SecureDesktopPlaySound) { UIUtil.PlayUacSound(); } // bLangBar = ShowLangBar(true); lock (stp) { stp.State = SecureThreadState.ShowingDialog; } stp.DialogResult = f.ShowDialog(formBackPrimary); stp.ResultObject = m_fnResultBuilder(f); UIUtil.DestroyForm(f); } catch (Exception) { Debug.Assert(false); } finally { // if(bLangBar) ShowLangBar(false); foreach (BackgroundForm formBack in lBackForms) { try { formBack.Close(); UIUtil.DestroyForm(formBack); } catch (Exception) { Debug.Assert(false); } } lock (stp) { stp.State = SecureThreadState.Terminated; } } }
public DialogResult ShowDialog(out object objResult, object objConstructParam) { objResult = null; ProcessMessagesEx(); ClipboardEventChainBlocker ccb = new ClipboardEventChainBlocker(); byte[] pbClipHash = ClipboardUtil.ComputeHash(); Bitmap bmpBack = UIUtil.CreateScreenshot(); if(bmpBack != null) UIUtil.DimImage(bmpBack); DialogResult dr = DialogResult.None; try { uint uOrgThreadId = NativeMethods.GetCurrentThreadId(); IntPtr pOrgDesktop = NativeMethods.GetThreadDesktop(uOrgThreadId); string strName = "D" + Convert.ToBase64String( CryptoRandom.Instance.GetRandomBytes(16), Base64FormattingOptions.None); strName = strName.Replace(@"+", string.Empty); strName = strName.Replace(@"/", string.Empty); strName = strName.Replace(@"=", string.Empty); if(strName.Length > 15) strName = strName.Substring(0, 15); NativeMethods.DesktopFlags deskFlags = (NativeMethods.DesktopFlags.CreateMenu | NativeMethods.DesktopFlags.CreateWindow | NativeMethods.DesktopFlags.ReadObjects | NativeMethods.DesktopFlags.WriteObjects | NativeMethods.DesktopFlags.SwitchDesktop); IntPtr pNewDesktop = NativeMethods.CreateDesktop(strName, null, IntPtr.Zero, 0, deskFlags, IntPtr.Zero); if(pNewDesktop == IntPtr.Zero) throw new InvalidOperationException(); bool bNameSupported = NativeMethods.DesktopNameContains(pNewDesktop, strName).GetValueOrDefault(false); Debug.Assert(bNameSupported); SecureThreadInfo stp = new SecureThreadInfo(); stp.BackgroundBitmap = bmpBack; stp.ThreadDesktop = pNewDesktop; stp.FormConstructParam = objConstructParam; Thread th = new Thread(this.SecureDialogThread); th.CurrentCulture = Thread.CurrentThread.CurrentCulture; th.CurrentUICulture = Thread.CurrentThread.CurrentUICulture; th.Start(stp); SecureThreadState st = SecureThreadState.None; while(st != SecureThreadState.Terminated) { th.Join(150); lock(stp) { st = stp.State; } if((st == SecureThreadState.ShowingDialog) && bNameSupported) { IntPtr hCurDesk = NativeMethods.OpenInputDesktop(0, false, NativeMethods.DesktopFlags.ReadObjects); if(hCurDesk == IntPtr.Zero) { Debug.Assert(false); continue; } if(hCurDesk == pNewDesktop) { if(!NativeMethods.CloseDesktop(hCurDesk)) { Debug.Assert(false); } continue; } bool? obOnSec = NativeMethods.DesktopNameContains(hCurDesk, strName); if(!NativeMethods.CloseDesktop(hCurDesk)) { Debug.Assert(false); } lock(stp) { st = stp.State; } // Update; might have changed if(obOnSec.HasValue && !obOnSec.Value && (st == SecureThreadState.ShowingDialog)) HandleUnexpectedDesktopSwitch(pOrgDesktop, pNewDesktop, stp); } } if(!NativeMethods.SwitchDesktop(pOrgDesktop)) { Debug.Assert(false); } NativeMethods.SetThreadDesktop(pOrgDesktop); th.Join(); // Ensure thread terminated before closing desktop if(!NativeMethods.CloseDesktop(pNewDesktop)) { Debug.Assert(false); } NativeMethods.CloseDesktop(pOrgDesktop); // Optional dr = stp.DialogResult; objResult = stp.ResultObject; } catch(Exception) { Debug.Assert(false); } byte[] pbNewClipHash = ClipboardUtil.ComputeHash(); if((pbClipHash != null) && (pbNewClipHash != null) && !MemUtil.ArraysEqual(pbClipHash, pbNewClipHash)) ClipboardUtil.Clear(); ccb.Release(); if(bmpBack != null) bmpBack.Dispose(); // If something failed, show the dialog on the normal desktop if(dr == DialogResult.None) { Form f = m_fnConstruct(objConstructParam); dr = f.ShowDialog(); objResult = m_fnResultBuilder(f); UIUtil.DestroyForm(f); } return dr; }
private static void HandleUnexpectedDesktopSwitch(IntPtr pOrgDesktop, IntPtr pNewDesktop, SecureThreadInfo stp) { NativeMethods.SwitchDesktop(pOrgDesktop); NativeMethods.SetThreadDesktop(pOrgDesktop); ProcessMessagesEx(); // Do not use MessageService.ShowWarning, because // it uses the top form's thread MessageService.ExternalIncrementMessageCount(); NativeMethods.MessageBoxFlags mbf = (NativeMethods.MessageBoxFlags.MB_ICONWARNING | NativeMethods.MessageBoxFlags.MB_TASKMODAL | NativeMethods.MessageBoxFlags.MB_SETFOREGROUND | NativeMethods.MessageBoxFlags.MB_TOPMOST); if(StrUtil.RightToLeft) mbf |= (NativeMethods.MessageBoxFlags.MB_RTLREADING | NativeMethods.MessageBoxFlags.MB_RIGHT); NativeMethods.MessageBox(IntPtr.Zero, KPRes.SecDeskOtherSwitched + MessageService.NewParagraph + KPRes.SecDeskSwitchBack, PwDefs.ShortProductName, mbf); MessageService.ExternalDecrementMessageCount(); SecureThreadState st; lock(stp) { st = stp.State; } if(st != SecureThreadState.Terminated) { NativeMethods.SwitchDesktop(pNewDesktop); ProcessMessagesEx(); } }
private void SecureDialogThread(object oParam) { BackgroundForm formBack = null; SecureThreadInfo stp = (oParam as SecureThreadInfo); if (stp == null) { Debug.Assert(false); return; } try { if (!NativeMethods.SetThreadDesktop(stp.ThreadDesktop)) { Debug.Assert(false); return; } ProcessMessagesEx(); // Test whether we're really on the secure desktop if (NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId()) != stp.ThreadDesktop) { Debug.Assert(false); return; } // Disabling IME is not required anymore; we terminate // CtfMon.exe child processes manually // try { NativeMethods.ImmDisableIME(0); } // Always false on 2000/XP // catch(Exception) { Debug.Assert(!WinUtil.IsAtLeastWindows2000); } ProcessMessagesEx(); formBack = new BackgroundForm(stp.BackgroundBitmap); formBack.Show(); ProcessMessagesEx(); if (!NativeMethods.SwitchDesktop(stp.ThreadDesktop)) { Debug.Assert(false); } ProcessMessagesEx(); Form f = m_fnConstruct(stp.FormConstructParam); if (f == null) { Debug.Assert(false); return; } if (Program.Config.UI.SecureDesktopPlaySound) { UIUtil.PlayUacSound(); } lock (stp) { stp.State = SecureThreadState.ShowingDialog; } stp.DialogResult = f.ShowDialog(formBack); stp.ResultObject = m_fnResultBuilder(f); UIUtil.DestroyForm(f); } catch (Exception) { Debug.Assert(false); } finally { if (formBack != null) { try { formBack.Close(); UIUtil.DestroyForm(formBack); } catch (Exception) { Debug.Assert(false); } } lock (stp) { stp.State = SecureThreadState.Terminated; } } }
private void SecureDialogThread(object oParam) { SecureThreadInfo stp = (oParam as SecureThreadInfo); if (stp == null) { Debug.Assert(false); return; } List <BackgroundForm> lBackForms = new List <BackgroundForm>(); BackgroundForm formBackPrimary = null; // bool bLangBar = false; try { if (!NativeMethods.SetThreadDesktop(stp.ThreadDesktop)) { Debug.Assert(false); return; } ProcessMessagesEx(); // Test whether we're really on the secure desktop if (NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId()) != stp.ThreadDesktop) { Debug.Assert(false); return; } // Disabling the IME was not required, because we terminate // CtfMon.exe child processes manually. However, since Sept. 2019, // there is an IME bug resulting in a black screen and/or an // IME/CTF process with high CPU load; // https://sourceforge.net/p/keepass/bugs/1881/ // https://keepass.info/help/kb/sec_desk.html#ime try { ulong uif = Program.Config.UI.UIFlags; if ((uif & (ulong)AceUIFlags.SecureDesktopIme) == 0) { NativeMethods.ImmDisableIME(0); // Always false on 2000/XP } } catch (Exception) { Debug.Assert(!WinUtil.IsAtLeastWindows2000); } ProcessMessagesEx(); Screen[] vScreens = Screen.AllScreens; Screen scPrimary = Screen.PrimaryScreen; Debug.Assert(vScreens.Length == stp.BackgroundBitmaps.Count); int sMin = Math.Min(vScreens.Length, stp.BackgroundBitmaps.Count); for (int i = sMin - 1; i >= 0; --i) { Bitmap bmpBack = stp.BackgroundBitmaps[i]; if (bmpBack == null) { continue; } Debug.Assert(bmpBack.Size == vScreens[i].Bounds.Size); BackgroundForm formBack = new BackgroundForm(bmpBack, vScreens[i]); lBackForms.Add(formBack); if (vScreens[i].Equals(scPrimary)) { formBackPrimary = formBack; } formBack.Show(); } if (formBackPrimary == null) { Debug.Assert(false); if (lBackForms.Count > 0) { formBackPrimary = lBackForms[lBackForms.Count - 1]; } } ProcessMessagesEx(); if (!NativeMethods.SwitchDesktop(stp.ThreadDesktop)) { Debug.Assert(false); } ProcessMessagesEx(); Form f = m_fnConstruct(stp.FormConstructParam); if (f == null) { Debug.Assert(false); return; } if (Program.Config.UI.SecureDesktopPlaySound) { UIUtil.PlayUacSound(); } // bLangBar = ShowLangBar(true); lock (stp) { stp.State = SecureThreadState.ShowingDialog; } stp.DialogResult = f.ShowDialog(formBackPrimary); stp.ResultObject = m_fnResultBuilder(f); UIUtil.DestroyForm(f); } catch (Exception) { Debug.Assert(false); } finally { // if(bLangBar) ShowLangBar(false); foreach (BackgroundForm formBack in lBackForms) { try { formBack.Close(); UIUtil.DestroyForm(formBack); } catch (Exception) { Debug.Assert(false); } } lock (stp) { stp.State = SecureThreadState.Terminated; } } }