1 |
/* wptSafeEditCtrl.cpp - Safe edit control for passwords |
2 |
* Copyright (C) 2006 Timo Schulz, g10 Code GmbH |
3 |
* |
4 |
* This file is part of WinPT. |
5 |
* |
6 |
* WinPT is free software; you can redistribute it and/or modify |
7 |
* it under the terms of the GNU General Public License as published by |
8 |
* the Free Software Foundation; either version 2 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* WinPT is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with WinPT; if not, write to the Free Software Foundation, |
18 |
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
19 |
*/ |
20 |
|
21 |
#include <windows.h> |
22 |
#include <stdio.h> |
23 |
#include <richedit.h> |
24 |
#include "resource.h" |
25 |
|
26 |
#include "wptTypes.h" |
27 |
#include "wptW32API.h" |
28 |
|
29 |
/* New message to retrieve text. */ |
30 |
#define WM_SAFE_GETTEXT (WM_USER+11) |
31 |
|
32 |
/* Hook context. */ |
33 |
struct hook_ctx_s { |
34 |
HHOOK keyb; /* handle for the keyboard. */ |
35 |
HHOOK cbt; /* handle for computer based training (CBT) */ |
36 |
}; |
37 |
typedef struct hook_ctx_s *hook_ctx_t; |
38 |
|
39 |
/* Subclassing context. */ |
40 |
struct subclass_ctx_s { |
41 |
WNDPROC new_wnd_fnc; /* old window procedure. */ |
42 |
WNDPROC old_wnd_fnc; /* new window procedure. */ |
43 |
HWND wnd; /* window handle that is subclassed. */ |
44 |
void *opaque; |
45 |
}; |
46 |
typedef struct subclass_ctx_s *subclass_ctx_t; |
47 |
|
48 |
|
49 |
/* Dummy hook procedure to avoid channing. */ |
50 |
static LRESULT CALLBACK |
51 |
dummy_hook_proc (int code, WPARAM wparam, LPARAM lparam) |
52 |
{ |
53 |
return FALSE; |
54 |
} |
55 |
|
56 |
|
57 |
/* Set dummy hooks to prevent that the control is monitored |
58 |
by some external hook functions. */ |
59 |
static hook_ctx_t |
60 |
install_dummy_hooks (void) |
61 |
{ |
62 |
hook_ctx_t hc; |
63 |
|
64 |
hc = (hook_ctx_t)calloc (1, sizeof *hc); |
65 |
if (!hc) |
66 |
return NULL; |
67 |
hc->cbt = SetWindowsHookEx (WH_CBT, dummy_hook_proc, |
68 |
GetModuleHandle (NULL), 0); |
69 |
if (hc->cbt == NULL) { |
70 |
free (hc); |
71 |
return NULL; |
72 |
} |
73 |
hc->keyb = SetWindowsHookEx (WH_KEYBOARD, dummy_hook_proc, |
74 |
GetModuleHandle (NULL), 0); |
75 |
if (hc->keyb == NULL) { |
76 |
free (hc); |
77 |
return NULL; |
78 |
} |
79 |
return hc; |
80 |
} |
81 |
|
82 |
|
83 |
/* Remove dummy hooks and free memory. */ |
84 |
static void |
85 |
deinstall_dummy_hooks (hook_ctx_t hc) |
86 |
{ |
87 |
UnhookWindowsHookEx (hc->cbt); |
88 |
UnhookWindowsHookEx (hc->keyb); |
89 |
free (hc); |
90 |
} |
91 |
|
92 |
|
93 |
/* Filtered window procedure for the passphrase edit control. */ |
94 |
static LRESULT CALLBACK |
95 |
safe_edit_dlg_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) |
96 |
{ |
97 |
static subclass_ctx_t ctx = NULL; |
98 |
|
99 |
/* XXX: filter all messages which were not send from |
100 |
the current process. */ |
101 |
|
102 |
if (ctx == NULL) |
103 |
ctx = (subclass_ctx_t)GetWindowLong (hwnd, GWL_USERDATA); |
104 |
|
105 |
switch (msg) { |
106 |
case WM_CUT: |
107 |
case WM_COPY: |
108 |
case WM_PASTE: |
109 |
/* do not allow to copy or paste the inserted text. */ |
110 |
return TRUE; |
111 |
|
112 |
case WM_KEYUP: |
113 |
case WM_KEYDOWN: |
114 |
/* XXX: maybe in a future release, we might want to provide |
115 |
our own buffer implementation. */ |
116 |
|
117 |
/* ignore 'end' and 'home' to make text selection more difficult. */ |
118 |
if ((int)wparam == VK_END || (int)wparam == VK_HOME) |
119 |
return TRUE; |
120 |
break; |
121 |
|
122 |
case WM_GETTEXT: |
123 |
/* disallow normal gettext message. */ |
124 |
return TRUE; |
125 |
|
126 |
case WM_SAFE_GETTEXT: |
127 |
{ /* transform WM_GETTEXT request into EM_GETLINE request. */ |
128 |
char *buffer = (char*)wparam; |
129 |
int n; |
130 |
buffer[0] = (char)lparam; |
131 |
n = SendMessage (hwnd, EM_GETLINE, 0, wparam); |
132 |
buffer[n] = 0; |
133 |
} |
134 |
break; |
135 |
|
136 |
case EM_GETLINE: |
137 |
break; |
138 |
|
139 |
case WM_LBUTTONDBLCLK: |
140 |
case WM_RBUTTONUP: |
141 |
case WM_RBUTTONDOWN: |
142 |
/* prevent that the popup menu is displayed. */ |
143 |
return TRUE; |
144 |
|
145 |
case EM_SETSEL: |
146 |
case EM_SCROLLCARET: |
147 |
case EM_FINDTEXT: |
148 |
case EM_FINDTEXTEX: |
149 |
case EM_GETSELTEXT: |
150 |
case EM_PASTESPECIAL: |
151 |
case EM_GETTEXTRANGE: |
152 |
case EM_STREAMIN: |
153 |
case EM_STREAMOUT: |
154 |
/* disallow message which could be used to copy the text. */ |
155 |
return TRUE; |
156 |
} |
157 |
|
158 |
return CallWindowProc (ctx->old_wnd_fnc, hwnd, msg, wparam, lparam); |
159 |
} |
160 |
|
161 |
|
162 |
/* Sublclass the window in the dialog @dlg with the id @ctrlid. |
163 |
@new_wnd_fnc is the new window procedure. */ |
164 |
static int |
165 |
subclass_window (HWND dlg, int ctrlid, WNDPROC new_wnd_fnc, |
166 |
subclass_ctx_t *r_ctx) |
167 |
{ |
168 |
subclass_ctx_t ctx; |
169 |
|
170 |
*r_ctx = NULL; |
171 |
ctx = (subclass_ctx_t)calloc (1, sizeof *ctx); |
172 |
if (!ctx) |
173 |
return -1; |
174 |
ctx->wnd = GetDlgItem (dlg, ctrlid); |
175 |
ctx->new_wnd_fnc = new_wnd_fnc; |
176 |
ctx->old_wnd_fnc = (WNDPROC)GetWindowLong (ctx->wnd, GWL_WNDPROC); |
177 |
if (!ctx->old_wnd_fnc) { |
178 |
free (ctx); |
179 |
return -1; |
180 |
} |
181 |
SetLastError (0); |
182 |
SetWindowLong (ctx->wnd, GWL_WNDPROC, (LONG)new_wnd_fnc); |
183 |
if (GetLastError () != 0) { |
184 |
free (ctx); |
185 |
return -1; |
186 |
} |
187 |
SetLastError (0); |
188 |
SetWindowLong (ctx->wnd, GWL_USERDATA, (LONG)ctx); |
189 |
if (GetLastError () != 0) { |
190 |
free (ctx); |
191 |
return -1; |
192 |
} |
193 |
if (r_ctx) |
194 |
*r_ctx = ctx; |
195 |
return 0; |
196 |
} |
197 |
|
198 |
|
199 |
/* Restore the old window procedure for the subclassed control |
200 |
and free the memory. */ |
201 |
static void |
202 |
subclass_free_memory (HWND dlg, int ctrlid) |
203 |
{ |
204 |
subclass_ctx_t ctx; |
205 |
HWND wnd; |
206 |
|
207 |
wnd = GetDlgItem (dlg, ctrlid); |
208 |
ctx = (subclass_ctx_t)GetWindowLong (wnd, GWL_USERDATA); |
209 |
SetWindowLong (ctx->wnd, GWL_WNDPROC, (LONG)ctx->old_wnd_fnc); |
210 |
if (ctx != NULL) |
211 |
free (ctx); |
212 |
} |
213 |
|
214 |
|
215 |
/* Init subclassing to provide a safer edit control |
216 |
for the dialog @dlg control @ctlid. */ |
217 |
void |
218 |
safe_edit_control_init (HWND dlg, int ctlid) |
219 |
{ |
220 |
subclass_ctx_t ctx; |
221 |
|
222 |
subclass_window (dlg, ctlid, safe_edit_dlg_proc, &ctx); |
223 |
if (ctx != NULL) |
224 |
ctx->opaque = install_dummy_hooks (); |
225 |
} |
226 |
|
227 |
|
228 |
/* Reset subclassing for the edit control @ctlid. */ |
229 |
void |
230 |
safe_edit_control_free (HWND dlg, int ctlid) |
231 |
{ |
232 |
subclass_ctx_t ctx; |
233 |
HWND wnd; |
234 |
|
235 |
wnd = GetDlgItem (dlg, ctlid); |
236 |
ctx = (subclass_ctx_t)GetWindowLong (wnd, GWL_USERDATA); |
237 |
if (ctx != NULL && ctx->opaque != NULL) |
238 |
deinstall_dummy_hooks ((hook_ctx_t)ctx->opaque); |
239 |
subclass_free_memory (dlg, ctlid); |
240 |
} |
241 |
|
242 |
|
243 |
UINT |
244 |
SafeGetDlgItemText (HWND dlg, int id, char *buf, int buflen) |
245 |
{ |
246 |
/* XXX: use memset (buf, 0, buflen)? */ |
247 |
SendDlgItemMessage (dlg, id, WM_SAFE_GETTEXT, (WPARAM)buf, (LPARAM)buflen); |
248 |
return strlen (buf); |
249 |
} |