1
/* main.c - Secure W32 dialog for PIN entry.
2
Copyright (C) 2004, 2007 g10 Code GmbH
4
This program is free software; you can redistribute it and/or
5
modify it under the terms of the GNU General Public License as
6
published by the Free Software Foundation; either version 2 of the
7
License, or (at your option) any later version.
9
This program is distributed in the hope that it will be useful, but
10
WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22
#define WINVER 0x0403 /* Required for SendInput. */
29
/* #include "msgcodes.h" */
31
#define PGMNAME "pinentry-w32"
35
# define LSFW_UNLOCK 2
39
/* This function pointer gets initialized in main. */
40
static WINUSERAPI BOOL WINAPI (*lock_set_foreground_window)(UINT);
43
static int w32_cmd_handler (pinentry_t pe);
44
static void ok_button_clicked (HWND dlg, pinentry_t pe);
47
/* We use global variables for the state, because there should never
48
ever be a second instance. */
49
static HWND dialog_handle;
50
static int confirm_mode;
51
static int passphrase_ok;
52
static int confirm_yes;
57
/* Connect this module to the pinnetry framework. */
58
pinentry_cmd_handler_t pinentry_cmd_handler = w32_cmd_handler;
65
static char strerr[256];
68
ec = (int)GetLastError ();
69
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec,
70
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
71
strerr, sizeof strerr - 1, NULL);
77
/* show_window_hierarchy (HWND parent, int level) */
81
/* child = GetWindow (parent, GW_CHILD); */
84
/* char buf[1024+1]; */
89
/* memset (buf, 0, sizeof (buf)); */
90
/* GetWindowText (child, buf, sizeof (buf)-1); */
91
/* nname = GetClassName (child, name, sizeof (name)-1); */
96
/* fprintf (debugfp, "### %*shwnd=%p (%s) `%s'\n", level*2, "", child, */
97
/* pname? pname:"", buf); */
98
/* show_window_hierarchy (child, level+1); */
99
/* child = GetNextWindow (child, GW_HWNDNEXT); */
107
/* Convert a wchar to UTF8. Caller needs to release the string.
108
Returns NULL on error. */
110
wchar_to_utf8 (const wchar_t *string, size_t len, int secure)
115
/* Note, that CP_UTF8 is not defined in Windows versions earlier
117
n = WideCharToMultiByte (CP_UTF8, 0, string, len, NULL, 0, NULL, NULL);
121
result = secure? secmem_malloc (n+1) : malloc (n+1);
124
n = WideCharToMultiByte (CP_UTF8, 0, string, len, result, n, NULL, NULL);
128
secmem_free (result);
137
/* Convert a UTF8 string to wchar. Returns NULL on error. Caller
138
needs to free the returned value. */
140
utf8_to_wchar (const char *string)
144
size_t len = strlen (string);
146
n = MultiByteToWideChar (CP_UTF8, 0, string, len, NULL, 0);
150
result = calloc ((n+1), sizeof *result);
153
n = MultiByteToWideChar (CP_UTF8, 0, string, len, result, n);
164
/* Center the window CHILDWND with the desktop as its parent
165
window. STYLE is passed as second arg to SetWindowPos.*/
167
center_window (HWND childwnd, HWND style)
170
RECT rchild, rparent;
172
int wchild, hchild, wparent, hparent;
173
int wscreen, hscreen, xnew, ynew;
174
int flags = SWP_NOSIZE | SWP_NOZORDER;
176
parwnd = GetDesktopWindow ();
177
GetWindowRect (childwnd, &rchild);
178
wchild = rchild.right - rchild.left;
179
hchild = rchild.bottom - rchild.top;
181
GetWindowRect (parwnd, &rparent);
182
wparent = rparent.right - rparent.left;
183
hparent = rparent.bottom - rparent.top;
185
hdc = GetDC (childwnd);
186
wscreen = GetDeviceCaps (hdc, HORZRES);
187
hscreen = GetDeviceCaps (hdc, VERTRES);
188
ReleaseDC (childwnd, hdc);
189
xnew = rparent.left + ((wparent - wchild) / 2);
192
else if ((xnew+wchild) > wscreen)
193
xnew = wscreen - wchild;
194
ynew = rparent.top + ((hparent - hchild) / 2);
197
else if ((ynew+hchild) > hscreen)
198
ynew = hscreen - hchild;
199
if (style == HWND_TOPMOST || style == HWND_NOTOPMOST)
200
flags = SWP_NOMOVE | SWP_NOSIZE;
201
SetWindowPos (childwnd, style? style : NULL, xnew, ynew, 0, 0, flags);
207
move_mouse_and_click (HWND hwnd)
211
int wscreen, hscreen, x, y, normx, normy;
216
wscreen = GetDeviceCaps (hdc, HORZRES);
217
hscreen = GetDeviceCaps (hdc, VERTRES);
218
ReleaseDC (hwnd, hdc);
219
if (wscreen < 10 || hscreen < 10)
222
GetWindowRect (hwnd, &rect);
226
normx = x * (65535 / wscreen);
227
if (normx < 0 || normx > 65535)
229
normy = y * (65535 / hscreen);
230
if (normy < 0 || normy > 65535)
233
for (idx=0; idx < 3; idx++)
234
memset (&inp[idx], 0, sizeof inp[idx]);
237
inp[idx].type = INPUT_MOUSE;
238
inp[idx].mi.dx = normx;
239
inp[idx].mi.dy = normy;
240
inp[idx].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
243
inp[idx].type = INPUT_MOUSE;
244
inp[idx].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
247
inp[idx].type = INPUT_MOUSE;
248
inp[idx].mi.dwFlags = MOUSEEVENTF_LEFTUP;
251
if ( SendInput (idx, inp, sizeof (INPUT)) != idx && debugfp )
252
fprintf (debugfp, "SendInput failed: %s\n", w32_strerror (-1));
257
/* Resize the button so that STRING fits into it. */
259
resize_button (HWND hwnd, const char *string)
264
/* FIXME: Need to figure out how to convert dialog coorddnates to
265
screen coordinates and how buttons should be placed. */
266
/* SetWindowPos (hbutton, NULL, */
268
/* strlen (string+2), 14, */
269
/* (SWP_NOZORDER)); */
276
/* Call SetDlgItemTextW with an UTF8 string. */
278
set_dlg_item_text (HWND dlg, int item, const char *string)
280
if (!string || !*string)
281
SetDlgItemText (dlg, item, "");
286
wbuf = utf8_to_wchar (string);
288
SetDlgItemText (dlg, item, "[out of core]");
291
SetDlgItemTextW (dlg, item, wbuf);
298
/* Dialog processing loop. */
300
dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
302
static pinentry_t pe;
309
/* for (idx=0; msgcodes[idx].string; idx++) */
310
/* if (msg == msgcodes[idx].msg) */
312
/* if (msgcodes[idx].string) */
313
/* fprintf (debugfp, "received %s\n", msgcodes[idx].string); */
315
/* fprintf (debugfp, "received WM_%u\n", msg); */
322
pe = (pinentry_t)lparam;
325
set_dlg_item_text (dlg, IDC_PINENT_PROMPT, pe->prompt);
326
set_dlg_item_text (dlg, IDC_PINENT_DESC, pe->description);
327
set_dlg_item_text (dlg, IDC_PINENT_TEXT, "");
330
set_dlg_item_text (dlg, IDOK, pe->ok);
331
resize_button (GetDlgItem (dlg, IDOK), pe->ok);
335
set_dlg_item_text (dlg, IDCANCEL, pe->cancel);
336
resize_button (GetDlgItem (dlg, IDCANCEL), pe->cancel);
339
set_dlg_item_text (dlg, IDC_PINENT_ERR, pe->error);
343
EnableWindow (GetDlgItem (dlg, IDC_PINENT_TEXT), FALSE);
344
SetWindowPos (GetDlgItem (dlg, IDC_PINENT_TEXT), NULL, 0, 0, 0, 0,
345
(SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW));
350
item = IDC_PINENT_TEXT;
352
center_window (dlg, HWND_TOP);
354
/* Unfortunately we can't use SetForegroundWindow because there
355
is no easy eay to have all the calling processes do an
356
AllowSetForegroundWindow. What we do instead is to bad hack
357
by simulating a click to the Window. */
358
/* if (SetForegroundWindow (dlg) && lock_set_foreground_window) */
360
/* lock_set_foreground_window (LSFW_LOCK); */
363
/* show_window_hierarchy (GetDesktopWindow (), 0); */
365
ShowWindow (dlg, SW_SHOW);
366
move_mouse_and_click ( GetDlgItem (dlg, IDC_PINENT_PROMPT) );
371
switch (LOWORD (wparam))
377
ok_button_clicked (dlg, pe);
378
EndDialog (dlg, TRUE);
383
EndDialog (dlg, FALSE);
392
/* The okay button has been clicked or the enter enter key in the text
395
ok_button_clicked (HWND dlg, pinentry_t pe)
399
size_t w_buffer_size = 255;
403
w_buffer = secmem_malloc ((w_buffer_size + 1) * sizeof *w_buffer);
407
nchar = GetDlgItemTextW (dlg, IDC_PINENT_TEXT, w_buffer, w_buffer_size);
408
s_utf8 = wchar_to_utf8 (w_buffer, nchar, 1);
409
secmem_free (w_buffer);
413
pinentry_setbufferlen (pe, strlen (s_utf8) + 1);
415
strcpy (pe->pin, s_utf8);
416
secmem_free (s_utf8);
418
pe->result = pe->pin? strlen (pe->pin) : 0;
424
w32_cmd_handler (pinentry_t pe)
426
/* HWND lastwindow = GetForegroundWindow (); */
428
confirm_mode = !pe->pin;
430
passphrase_ok = confirm_yes = 0;
432
dialog_handle = NULL;
433
DialogBoxParam (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_PINENT),
434
GetDesktopWindow (), dlg_proc, (LPARAM)pe);
437
/* if (lock_set_foreground_window) */
438
/* lock_set_foreground_window (LSFW_UNLOCK); */
439
/* if (lastwindow) */
440
/* SetForegroundWindow (lastwindow); */
447
else if (passphrase_ok && pe->pin)
448
return strlen (pe->pin);
455
main (int argc, char **argv)
459
pinentry_init (PGMNAME);
461
/* Consumes all arguments. */
462
if (pinentry_parse_opts (argc, argv))
464
printf ("pinentry-w32 (pinentry) " VERSION "\n");
468
/* debugfp = fopen ("pinentry.log", "w"); */
470
/* debugfp = stderr; */
472
/* We need to load a function because that one is only available
473
since W2000 but not in older NTs. */
474
handle = LoadLibrary ("user32.dll");
478
foo = GetProcAddress (handle, "LockSetForegroundWindow");
480
lock_set_foreground_window = foo;
482
CloseHandle (handle);
485
if (pinentry_loop ())